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

NeuralMarketsNetworkToolkitLibrary "NeuralMarketsNetworkToolkit"
Open-source network analysis toolkit for Pine Script.
This library provides reusable graph algorithms, matrix utilities and network analytics for building advanced multi-asset indicators. Rather than treating markets as isolated charts, it enables developers to model relationships between assets as weighted networks and extract structural characteristics such as connectivity, centrality, clustering and influence.
Current Modules
• Matrix utilities
• Directed & undirected graphs
• Network analytics
• Node analytics
• Graph algorithms
• Experimental financial network tools
Example Applications
• Correlation networks
• Market leadership analysis
• Sector relationship maps
• Cross-asset dependency analysis
• Financial network research
Design Philosophy
This toolkit provides reusable quantitative building blocks rather than trading signals. Functions are intentionally modular so they can be combined into custom indicators and research projects.
Markets are networks. This toolkit provides the building blocks to analyze them as such.
--------------------------------------------------------------------
matrixIndex(row, col, n)
Converts row/column coordinates into a flat matrix index.
Parameters:
row (int) : Row index.
col (int) : Column index.
n (int) : Matrix dimension.
Returns: Flat-array index.
clamp(x, lo, hi)
Clamp a float.
Parameters:
x (float) : Value.
lo (float) : Minimum.
hi (float) : Maximum.
Returns: Clamped value.
newMatrix(n, initialValue)
Creates an n x n flat matrix initialized to a value.
Parameters:
n (int) : Number of nodes.
initialValue (float) : Initial cell value.
Returns: Flat float array.
setCell(matrix, row, col, n, value)
Sets a matrix cell.
Parameters:
matrix (array) : Flat matrix.
row (int) : Row.
col (int) : Column.
n (int) : Matrix dimension.
value (float) : New value.
getCell(matrix, row, col, n)
Gets a matrix cell.
Parameters:
matrix (array) : Flat matrix.
row (int) : Row.
col (int) : Column.
n (int) : Matrix dimension.
Returns: Cell value.
setUndirectedEdge(matrix, a, b, n, weight)
Sets both directions of an undirected edge.
Parameters:
matrix (array) : Flat matrix.
a (int) : Node A.
b (int) : Node B.
n (int) : Matrix dimension.
weight (float) : Edge weight.
meanAbsoluteConnectivity(matrix, n)
Average absolute pairwise edge weight.
Parameters:
matrix (array) : Symmetric adjacency/weight matrix.
n (int) : Number of nodes.
Returns: Average absolute connectivity from 0 upward.
meanSignedConnectivity(matrix, n)
Average signed pairwise weight.
Parameters:
matrix (array) : Symmetric matrix.
n (int) : Number of nodes.
Returns: Mean signed relationship.
density(matrix, n, threshold)
Proportion of possible edges whose absolute weight exceeds threshold.
Parameters:
matrix (array) : Symmetric weight matrix.
n (int) : Number of nodes.
threshold (float) : Absolute edge threshold.
Returns: Network density from 0 to 1.
fragmentation(matrix, n, threshold)
Network fragmentation as inverse threshold density.
Parameters:
matrix (array) : Symmetric weight matrix.
n (int) : Number of nodes.
threshold (float) : Edge threshold.
Returns: Fragmentation from 0 to 1.
nodeDegree(matrix, n, node, threshold)
Number of strong edges attached to a node.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
node (int) : Node index.
threshold (float) : Absolute edge threshold.
Returns: Degree count.
nodeStrength(matrix, n, node)
Sum of absolute edge weights attached to node.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Node strength.
strongestNode(matrix, n)
Node with greatest absolute network strength.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Strongest node index.
averageNodeStrength(matrix, n)
Average node strength.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Mean strength.
centralization(matrix, n)
Measures how much one node dominates the network.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Strength centralization approximately 0 to 1.
strengthEntropy(matrix, n)
Shannon entropy of node-strength distribution.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Normalized entropy from 0 to 1.
mstDistance(matrix, n)
Computes total Prim minimum-spanning-tree distance.
Similarity is converted to distance using 1 - abs(similarity).
Parameters:
matrix (array) : Similarity matrix.
n (int) : Number of nodes.
Returns: Total MST distance.
mstCompactness(matrix, n)
Converts MST distance to compactness.
Parameters:
matrix (array) : Similarity matrix.
n (int) : Number of nodes.
Returns: Network compactness from approximately 0 to 1.
setDirectedEdge(matrix, fromNode, toNode, n, weight)
Sets one directed edge.
Parameters:
matrix (array) : Flat directed adjacency matrix.
fromNode (int) : Source node.
toNode (int) : Destination node.
n (int) : Number of nodes.
weight (float) : Directed edge weight.
outStrength(matrix, n, node)
Sum of outgoing positive influence from a node.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Source node.
Returns: Total outbound influence.
inStrength(matrix, n, node)
Sum of incoming positive influence to a node.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Destination node.
Returns: Total inbound influence.
netInfluence(matrix, n, node)
Net directional leadership.
Positive means the node influences others more than it follows them.
Negative means the node behaves more like a follower.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Outbound minus inbound influence.
normalizedLeadership(matrix, n, node)
Normalized directional leadership score.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Score approximately from -1 to +1.
leadingNode(matrix, n)
Node with the largest net directional influence.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Node index.
followingNode(matrix, n)
Node with the greatest incoming influence.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Node index.
meanDirectedInfluence(matrix, n)
Average directed influence in the network.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Mean positive directed edge weight.
leadershipConcentration(matrix, n)
Concentration of outbound influence.
High values mean leadership is concentrated in fewer nodes.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Herfindahl-style concentration from 0 to 1.
connectedComponentsCount(matrix, n, threshold)
Counts connected components in an undirected threshold graph.
Parameters:
matrix (array) : Symmetric adjacency / similarity matrix.
n (int) : Number of nodes.
threshold (float) : Minimum absolute edge weight required to connect nodes.
Returns: Number of connected components.
localClusteringCoefficient(matrix, n, node, threshold)
Computes local clustering coefficient for one node.
Measures how interconnected the node's neighbors are.
Parameters:
matrix (array) : Symmetric similarity matrix.
n (int) : Number of nodes.
node (int) : Node index.
threshold (float) : Minimum absolute edge weight to define a connection.
Returns: Local clustering coefficient from 0 to 1.
averageClusteringCoefficient(matrix, n, threshold)
Computes mean clustering coefficient across all nodes.
Parameters:
matrix (array) : Symmetric similarity matrix.
n (int) : Number of nodes.
threshold (float) : Minimum absolute edge weight.
Returns: Average clustering coefficient from 0 to 1.
similarityDistance(similarity)
Converts similarity to graph distance.
Higher similarity becomes shorter distance.
Parameters:
similarity (float) : Edge similarity, typically from 0 to 1 in magnitude.
Returns: Distance from 0 to 1.
shortestPathDistance(matrix, n, source, target)
Dijkstra shortest-path distance between two nodes.
Uses distance = 1 - abs(similarity).
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
source (int) : Start node.
target (int) : End node.
Returns: Shortest path distance.
averagePathLength(matrix, n)
Average shortest-path distance across all node pairs.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
Returns: Mean shortest path distance.
eigenvectorCentrality(matrix, n, node, iterations)
Approximate eigenvector centrality for one node using power iteration.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
node (int) : Node index.
iterations (int) : Number of power iterations.
Returns: Approximate normalized centrality from 0 to 1.
eigenvectorLeader(matrix, n, iterations)
Returns node with highest eigenvector centrality.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
iterations (int) : Number of power iterations.
Returns: Node index.
nodeStrengthPercentile(matrix, n, node)
Cross-sectional percentile rank for a node's strength.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Percentile rank from 0 to 100.
nodeStrengthRank(matrix, n, node)
Returns the rank position of a node by strength.
Rank 1 means strongest.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: One-based rank.
networkCohesion(matrix, n, threshold)
Composite network cohesion score.
Combines connectivity, density and clustering coefficient.
Parameters:
matrix (array) : Symmetric similarity matrix.
n (int) : Number of nodes.
threshold (float) : Edge threshold.
Returns: Composite cohesion from 0 to 1. Library

Library

EdgeStatsLibrary "EdgeStats"
A win rate on its own is not evidence. This library supplies the four things that turn one into a claim you can defend, none of which Pine ships: a base rate to subtract, a sample size corrected for overlapping forward windows, a confidence interval that behaves at small n, and a p-value that knows how many settings you tried before you picked this one.
The argument in three lines, all from the same 60 wins out of 100:
assess(60, 100, 0.5, horizon = 1) p = 0.046 significant
assess(60, 100, 0.5, horizon = 10) p = 0.527 not significant
assess(60, 100, 0.5, horizon = 10, trials = 30) p = 1.000 nothing at all
Nothing changed about the data. What changed is being honest that ten-bar forward returns sampled every bar are not a hundred independent observations, and that the best of thirty settings is not the same evidence as the only setting you tried.
WHAT THE DEMO SHOWS
Added to a chart directly, the library grades an ordinary signal: close above a 50 EMA, judged on whether price is higher ten bars later, over the last 500 bars. On BTCUSD 1h at the time of writing that is a hit rate of 39.9% against a base rate of 50.4%, an edge of -10.5 percentage points, and a two-sided p of 0.297.
Read that carefully, because it is the whole point. The signal looks bad. It is not reliably bad. Twenty-five independent observations cannot separate -10.5 points from noise, and the interval runs from 23.3% to 59.3%. A tool that says "I cannot tell" when it cannot tell is the only kind worth having.
THREE HONEST CAVEATS
n / horizon is a rough correction, not a theorem. It assumes overlap is the dominant source of dependence between observations. Where returns are autocorrelated beyond the window it is still optimistic. Treat it as a floor on your uncertainty rather than a ceiling.
zFor bisects normCdf, which is itself an approximation, so it inherits that error: zFor(0.95) lands about 1.2e-6 below the textbook 1.9599640. Irrelevant in practice, but it is an approximation of an approximation and you should hear that from me rather than discover it.
roll() uses ta.cum internally, so its call site must execute on every bar. Called inside "if barstate.islast" it has one bar of history and returns nonsense, and no max_bars_back setting repairs that. This is a property of Pine functions rather than of this library, and it is worth knowing generally.
VERIFICATION
Every fixed-input value is plotted to the Data Window and two are printed on the chart, so you can check the arithmetic rather than trust it. Against Python statistics.NormalDist:
normCdf(1.96) 0.9750022 true 0.9750021
normCdf(-1.0) 0.1586553
zFor(0.95) 1.9599628 true 1.9599640
zFor(0.99) 2.5758313 true 2.5758293
wilson(60, 100, 1.96)
selectionAdjusted(0.05, 30) 0.7853612
Corrections welcome, particularly to the effective sample size treatment, which is the part I would most like to be wrong about.
REFERENCE
normCdf(x)
Standard normal cumulative distribution. Abramowitz and Stegun 26.2.17, absolute error below 7.5e-8 across the whole real line.
Parameters:
x (float) : Value to evaluate.
Returns: Probability that a standard normal variate is at most x.
zFor(conf)
Two-sided z multiplier for a confidence level. Bisects normCdf, so any level works rather than a lookup of the usual three.
Parameters:
conf (float) : Confidence level in (0, 1). 0.95 returns 1.9599628.
Returns: The z for which the central interval of that width has the given coverage.
@remark Converged to float precision against normCdf, which is itself an approximation, so the result inherits its error: zFor(0.95) lands about 1.2e-6 below the textbook 1.9599640. Irrelevant for anything you would do with it, but it is an approximation of an approximation and worth saying so.
nEff(n, horizon)
Effective independent sample size when observations use overlapping forward windows.
Parameters:
n (float) : Raw observation count.
horizon (int) : Length in bars of the forward window each observation measures.
Returns: n divided by the horizon, with the horizon floored at 1.
wilson(hits, n, z)
Wilson score interval for a proportion. Unlike the normal approximation it stays inside and stays sane when n is small or the rate sits near an edge.
Parameters:
hits (float) : Successful observations.
n (float) : Total observations. Pass an effective count here, not a raw bar count, when the windows overlap.
z (float) : Multiplier from zFor().
Returns: A tuple on the proportion, or when there is no sample.
selectionAdjusted(p, trials)
Sidak correction. If you searched k settings and reported the best one, the p-value you found is not the p-value that best one deserves.
Parameters:
p (float) : Uncorrected two-sided p-value.
trials (int) : Settings, symbols or variants searched before this one was chosen. Pass 1 if you did not search.
Returns: Probability of seeing something at least this good in k independent tries.
roll(src, len)
Rolling window sum valid from the first bar, unlike math.sum which stays na until the window fills. Useful for counting events over a lookback.
Parameters:
src (float) : Series to accumulate.
len (simple int) : Window length in bars.
Returns: Sum of the last len values of src.
@remark Uses ta.cum internally, so the CALL SITE must execute on every bar. Called inside `if barstate.islast` it has one bar of history and returns nonsense. That is a property of Pine functions rather than of this library, and no max_bars_back setting repairs it. len is `simple` so Pine can size the history buffer at compile time.
assess(hits, n, base, horizon, conf, trials)
The whole assessment in one call.
Parameters:
hits (float) : Observations where the signal was right.
n (float) : Total observations.
base (float) : Rate at which the same outcome occurred unconditionally over the same horizon. This is the number that makes an edge an edge.
horizon (int) : Bars in the forward window. Overlapping windows shrink the effective sample.
conf (float) : Confidence level for the interval, default 0.95.
trials (int) : Settings searched before choosing this one, default 1.
Returns: A Verdict.
describe(v)
One line of plain English for a Verdict, sized to drop straight into a table cell.
Parameters:
v (Verdict) : The Verdict to describe.
Returns: A human-readable summary, or "no sample" when there is nothing to say.
Verdict
Everything needed to decide whether a measured hit rate means anything.
Fields:
rate (series float) : Observed hit rate, 0 to 1.
base (series float) : Base rate the signal is measured against, 0 to 1.
edge (series float) : rate minus base, in percentage points.
n (series float) : Raw observation count as supplied.
nEff (series float) : Observation count after the overlapping-window correction.
lo (series float) : Lower confidence bound on rate, computed on nEff.
hi (series float) : Upper confidence bound on rate, computed on nEff.
z (series float) : Test statistic of rate against base.
p (series float) : Two-sided p-value, already Sidak-adjusted for the trials argument.
clears (series bool) : True when the interval on the rate excludes the base rate. Library

KC Institutional Core LibraryKC Institutional Core Library v1.0
KCInstitutionalCore is a reusable Pine Script v6 utility library created to support structured technical-analysis workflows without duplicating common helper logic across multiple indicators and strategies.
The library provides transparent and independently reusable functions for:
Score normalization and trade-quality grading
Premium, Discount and Equilibrium classification
Risk-to-reward calculation
Risk-based position-size estimation
Timeframe-aware trading-style classification
Adaptive higher-timeframe selection
Directional alignment analysis
Execution-blocker identification
The exported functions are deterministic utilities. They do not generate guaranteed trading signals, predict future price movement or execute trades.
Basic import example
import Kelly_Carter12/KCInstitutionalCore/1 as kc
string grade = kc.scoreToGrade(78)
string style = kc.tradeStyle(timeframe.in_seconds())
= kc.rangeLocation(close, ta.highest(high, 50), ta.lowest(low, 50))
The detailed function documentation below explains every exported function, parameter and return value.
Library "KCInstitutionalCore"
Reusable Pine Script v6 utilities for timeframe context, score grading, premium/discount classification, alignment, risk-to-reward and position-size calculations. Designed as a transparent helper library for indicators and strategies.
clamp(value, minimum, maximum)
Restricts a numeric value to the supplied minimum and maximum boundaries.
Parameters:
value (float) : Value to restrict.
minimum (float) : Lower boundary.
maximum (float) : Upper boundary.
Returns: The restricted value.
scoreToGrade(score)
Converts a numeric score into a concise quality grade.
Parameters:
score (float) : Score expressed on a 0–100 scale.
Returns: A grade string from AA to D.
normalizeScore(rawScore, maximumScore)
Normalizes a raw score to a 0–100 scale.
Parameters:
rawScore (float) : Current raw score.
maximumScore (float) : Maximum possible raw score.
Returns: Normalized score from 0 to 100, or na when maximumScore is not positive.
rangeLocation(price, rangeHigh, rangeLow)
Classifies the current price inside a supplied dealing range.
Parameters:
price (float) : Current or evaluated price.
rangeHigh (float) : Upper boundary of the range.
rangeLow (float) : Lower boundary of the range.
Returns: A tuple containing PREMIUM, DISCOUNT, or EQUILIBRIUM and the 0–100 range percentage.
riskReward(entry, stop, target)
Calculates reward-to-risk from entry, stop and target prices.
Parameters:
entry (float) : Entry price.
stop (float) : Stop-loss price.
target (float) : Target price.
Returns: Absolute reward-to-risk ratio, or na when the stop distance is zero.
positionSize(accountSize, riskPercent, entry, stop, pointValue)
Estimates position size from account risk and stop distance.
Parameters:
accountSize (float) : Account balance or planning capital.
riskPercent (float) : Percentage of account risked.
entry (float) : Entry price.
stop (float) : Stop-loss price.
pointValue (float) : Monetary value per price point for one unit.
Returns: Estimated units or lots according to the supplied pointValue, or na for invalid inputs.
tradeStyle(chartSeconds)
Maps chart duration in seconds to a general planning style.
Parameters:
chartSeconds (float) : Chart timeframe duration in seconds, normally supplied with timeframe.in_seconds().
Returns: SCALP, INTRADAY, SWING, or POSITION.
adaptiveTimeframes(chartSeconds)
Suggests two broader context timeframes from the chart duration.
Parameters:
chartSeconds (float) : Chart timeframe duration in seconds, normally supplied with timeframe.in_seconds().
Returns: A tuple containing primary and secondary context timeframe strings.
alignmentState(localBias, htfBias, mtfBias)
Summarizes local, higher-timeframe and multi-timeframe directional agreement.
Parameters:
localBias (int) : Local direction: 1 bullish, -1 bearish, 0 neutral.
htfBias (int) : Higher-timeframe direction: 1 bullish, -1 bearish, 0 neutral.
mtfBias (int) : Broader alignment direction: 1 bullish, -1 bearish, 0 neutral.
Returns: BULL ALIGNED, BEAR ALIGNED, PARTIAL, CONFLICT, or NEUTRAL.
executionBlocker(direction, htfBias, mtfBias, location, structureConfirmed, liquidityConfirmed, newsBlocked)
Returns the first material execution blocker in a transparent priority order.
Parameters:
direction (int) : Intended direction: 1 long, -1 short, 0 neutral.
htfBias (int) : Higher-timeframe direction: 1 bullish, -1 bearish, 0 neutral.
mtfBias (int) : Multi-timeframe direction: 1 bullish, -1 bearish, 0 neutral.
location (string) : PREMIUM, DISCOUNT, or EQUILIBRIUM.
structureConfirmed (bool) : True when the required structure event is confirmed.
liquidityConfirmed (bool) : True when the required liquidity event is confirmed.
newsBlocked (bool) : True when a manual news blackout is active.
Returns: A concise blocker description, or CLEAR when no listed blocker is active. Library

TargetExcursionLibLibrary "TargetExcursionLib"
Parent supplies origin price/scale, direction, and path high/low/close series.
Library derives no hidden source data.
Returns bands.ready, bands.status, bands.effectiveSupport, and bands.resolvedCount.
Before minimum support: status is exactly "band stats not ready yet" and all band levels are na.
f_input(direction, originPrice, originScale, predictionValid, directionProbability, externalReliability)
Construct a generic target input from series values.
Parameters:
direction (int)
originPrice (float)
originScale (float)
predictionValid (bool)
directionProbability (float)
externalReliability (float)
f_model_new(gridSize, outcomeCap, smoothing, halfLife, family, shrinkageAlpha, minSupport, supportScale, minTransparency, maxTransparency, transparencyGamma)
Construct an independent stateful model instance.
Parameters:
gridSize (int)
outcomeCap (float)
smoothing (float)
halfLife (float)
family (series DensityFamily)
shrinkageAlpha (float)
minSupport (float)
supportScale (float)
minTransparency (int)
maxTransparency (int)
transparencyGamma (float)
f_update(model, signal, pathHigh, pathLow, pathClose, horizon, currentBar, confirmed)
Parameters:
model (TargetModel)
signal (TargetInput)
pathHigh (float)
pathLow (float)
pathClose (float)
horizon (int)
currentBar (int)
confirmed (bool)
TargetInput
Fields:
direction (series int)
originPrice (series float)
originScale (series float)
predictionValid (series bool)
directionProbability (series float)
externalReliability (series float)
TargetBands
Fields:
ready (series bool)
status (series string)
direction (series int)
originPrice (series float)
originScale (series float)
mfeQ10 (series float)
mfeQ50 (series float)
mfeQ80 (series float)
mfeQ90 (series float)
mfeMode (series float)
maeQ10 (series float)
maeQ50 (series float)
maeQ80 (series float)
maeQ90 (series float)
maeMode (series float)
mfePriceQ10 (series float)
mfePriceQ50 (series float)
mfePriceQ80 (series float)
mfePriceQ90 (series float)
mfePriceMode (series float)
maePriceQ10 (series float)
maePriceQ50 (series float)
maePriceQ80 (series float)
maePriceQ90 (series float)
maePriceMode (series float)
effectiveSupport (series float)
intervalCoverageEstimate (series float)
reliability (series float)
transparency (series int)
pendingCount (series int)
resolvedCount (series int)
lastResolvedBar (series int)
PendingTarget
Fields:
originBar (series int)
resolutionBar (series int)
direction (series int)
originPrice (series float)
originScale (series float)
maxHigh (series float)
minLow (series float)
TargetModel
Fields:
pending (array)
pooledMae (array)
pooledMfe (array)
longMae (array)
longMfe (array)
shortMae (array)
shortMfe (array)
pooledMaeWeight (series float)
pooledMaeWeightSq (series float)
pooledMfeWeight (series float)
pooledMfeWeightSq (series float)
longMaeWeight (series float)
longMaeWeightSq (series float)
longMfeWeight (series float)
longMfeWeightSq (series float)
shortMaeWeight (series float)
shortMaeWeightSq (series float)
shortMfeWeight (series float)
shortMfeWeightSq (series float)
gridSize (series int)
outcomeCap (series float)
smoothing (series float)
halfLife (series float)
family (series DensityFamily)
shrinkageAlpha (series float)
minSupport (series float)
supportScale (series float)
minTransparency (series int)
maxTransparency (series int)
transparencyGamma (series float)
lastDecayBar (series int)
lastResolvedBar (series int)
resolvedCount (series int) Library

Library

Library

FractalMemoryLib [Jayadev Rana]FractalMemoryLib packages the pattern-memory engine used by the Fractal Memory Projection indicator and the Fractal Memory Strategy so any script can import it.
WHAT IT DOES
The library finds the historical window whose movement shape most resembles the most recent bars (mean squared distance between stdev-normalized log returns), replays what followed that window as a projected close path, and sizes stops and targets adaptively by volatility regime.
EXPORTED FUNCTIONS
logRet(src) - one-bar log return of a series.
bestMatch(src, winLen, scanDepth, gapAhead) - scans up to scanDepth bars back and returns the offset of the most similar window plus a 0-100 similarity score. gapAhead reserves bars after the match for a projection.
analogPath(src, offset, fcLen, scaleF) - array of fcLen projected closes built by replaying the returns that followed the match, rescaled by scaleF (for example current ATR over ATR at the match).
adaptiveR(atrLen, rankLen, base) - volatility-adaptive unit risk: ATR times (base plus its 0-1 percentile rank), plus the rank itself. Call on every bar.
volRegime(volRank) - "Low", "Normal" or "High" label from the rank.
targets(entry, dirSign, unitR, slMult) - stop loss and TP1/TP2/TP3 at 1R, 2R and 3R.
USAGE NOTES
Call adaptiveR on every bar for ta consistency. bestMatch and analogPath are loop-heavy; for display purposes call them on the last bar only, and make sure the chart has at least scanDepth plus gapAhead bars of history. When the library itself is added to a chart it draws a small demo projection line from the best analog.
The analog projection is a statistical reference to a similar past episode, not a prediction, and not financial advice. 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

Library

TR Utility Library v6 ForkTR Utility Helpers v6 Fork is an open-source Pine Script v6 compatibility fork based on the publicly available Traders_Reality_Lib originally published by TradersReality.
This publication is not the original Traders Reality library and is not presented as an official update from the original author. All original concept credit, authorship credit, and recognition for the underlying Traders Reality workflow belong to TradersReality and the original creator(s).
Original source reference:
Traders_Reality_Lib by TradersReality.
Purpose of this publication:
The purpose of this fork is to provide a Pine Script v6-compatible utility library structure for scripts that require reusable helper functions related to PVSRA-style candle classification, ADR/range calculations, session handling, labels, lines, pivots, vector candle zones, psychological levels, and market-session countdown utilities.
This is a developer utility library. It is not a standalone trading indicator, not a signal system, and not a strategy.
Main function groups:
1. PVSRA-style candle classification
The library includes helper functions for classifying candles using volume and candle-spread behavior. These functions can return candle colors, alert flags, average volume, volume-spread values, and related classification data.
2. ADR and range helpers
The library includes Average Daily Range helper functions and ADR-based high/low projection functions. These can support scripts that need daily range reference levels or range-based chart tools.
3. Session calculation utilities
The library includes functions for parsing session strings, calculating session start and end timestamps, and handling sessions that cross midnight.
4. Session drawing helpers
The library includes reusable drawing helpers for opening ranges, session highs/lows, midpoint lines, labels, and shaded session boxes.
5. Daily open and timeframe utilities
The library includes helper functions for detecting new bars on selected resolutions, retrieving the daily open, and converting price movement into pips.
6. Label, line, and pivot helpers
The library contains reusable functions for right-aligned labels, last-bar labels, dynamic horizontal lines, daily-open lines, and pivot-style chart levels.
7. Vector candle zone helpers
The library includes helper functions for creating, updating, trimming, and cleaning vector candle zone boxes.
8. Psychological level helpers
The library includes functions for calculating psychological high/low reference levels based on selected timing logic and market type.
9. Session countdown helpers
The library includes functions for formatting milliseconds into readable time strings and calculating countdown text for market-session timing.
How to use this library:
This library is intended for Pine developers who want to import reusable helper functions into their own open-source or private scripts.
Example use cases include:
* PVSRA-style candle tools
* ADR and daily range tools
* Session high/low indicators
* Opening range indicators
* Pivot and level drawing tools
* Vector candle zone tools
* Market-session countdown panels
This library does not generate buy or sell signals by itself. Any trading logic, alerts, entries, exits, or visual systems must be implemented in the script that imports this library.
Originality and reuse clarification:
This publication is primarily a compatibility fork and utility organization resource. It is not presented as a new original trading methodology.
The underlying Traders Reality concepts and original library work are credited to the original creator(s). This fork is published open-source so users can inspect the code and verify the changes.
Limitations:
This library is a developer utility and should not be interpreted as financial advice. It does not provide buy or sell recommendations, does not execute trades, and does not guarantee any trading result.
Users and developers are responsible for testing any script that imports this library and for verifying that all calculations, visual outputs, and trading decisions fit their own requirements.
Library

AuditProtocolAuditProtocol
Every indicator makes claims. "This signal has edge." "Price stays inside these bands." Almost none of them keep score. I built this library so keeping score becomes a three line habit: import it, register your claims, and let the tape settle them. Any indicator can wear a live, lookahead free audit of itself.
What's inside
Prequal is a prequential tracker for probability forecasts. You register a probability before the outcome and resolve it after the outcome is real, never the other way around. It tracks hit rate and the Brier score, the proper scoring rule for probability forecasts, plus Brier skill against a coin flip.
Coverage is for bands and intervals. If your band claims 90% containment, this tracks what it actually delivered, target next to realized, recent and lifetime.
Conformal learns the width multiplier that makes any band honest. Feed it your normalized residuals and ask it for k(). Your band, resized until the coverage is real. This is the split conformal quantile method, applied to whatever band you already draw.
Barrier settles setups by first touch: target barrier or stop barrier, whichever the tape hits first. A nod to triple barrier labeling. Ties inside a single bar go to the stop, conservative by design.
The Audit Score compresses it into one 0 to 100 number. Calibration earns up to 50 points. Demonstrated skill earns the other 50. So an honest indicator with zero edge sits near 50. That's the anchor: 50 is sea level, everything above it is earned, and anything well below it is miscalibrated.
The Receipt is the standard panel. Claims on the left, reality on the right.
How to use it
import YOUR_HANDLE/AuditProtocol/1 as ap
var pq = ap.newPrequal()
var c90 = ap.newCoverage(90)
if barstate.isconfirmed
ap.resolve(pq, close > close ) // settle yesterday's call
ap.resolveInterval(c90, close) // settle yesterday's band
ap.predict(pq, myProbabilityUp) // register today's call
ap.setInterval(c90, myLo, myHi) // register today's band
ap.panel(pq, c90, na, position.top_right)
//////////////////////////////////////////////////////////////////////
Library "AuditProtocol"
newPrequal(emaAlpha)
Creates a prequential tracker.
Parameters:
emaAlpha (float) : EMA rate for the live (recent) readings. Default 0.02.
Returns: A fresh Prequal tracker.
method predict(this, p)
Registers a probability forecast (P of the outcome being TRUE) for the NEXT resolution. Call AFTER resolve() in the same confirmed-bar block.
Namespace types: Prequal
Parameters:
this (Prequal)
p (float)
method resolve(this, outcome)
Resolves the pending forecast against the realized outcome. Call once per confirmed bar BEFORE registering the next forecast.
Namespace types: Prequal
Parameters:
this (Prequal)
outcome (bool)
method hitRate(this)
Lifetime hit rate in percent, or na before any resolution.
Namespace types: Prequal
Parameters:
this (Prequal)
method skill(this)
Brier skill score vs the coin-flip baseline: 1 - Brier/0.25. 0 = no skill, 1 = perfect, negative = worse than guessing.
Namespace types: Prequal
Parameters:
this (Prequal)
newCoverage(targetPct, emaAlpha)
Creates a coverage tracker for a band claiming `targetPct` percent containment.
Parameters:
targetPct (float)
emaAlpha (float)
method setInterval(this, lo, hi)
Registers the band that should contain the NEXT observation.
Namespace types: Coverage
Parameters:
this (Coverage)
lo (float)
hi (float)
method resolveInterval(this, x)
Resolves the pending band against the realized value.
Namespace types: Coverage
Parameters:
this (Coverage)
x (float)
method realized(this)
Lifetime realized coverage in percent, or na before any resolution.
Namespace types: Coverage
Parameters:
this (Coverage)
method covError(this)
Absolute calibration error in percentage points: |realized - target|. na before any resolution.
Namespace types: Coverage
Parameters:
this (Coverage)
newConformal(targetPct, window, warmup)
Creates a conformal scaler. Feed it |realized error| / your band's unit width; ask it for k().
Parameters:
targetPct (float)
window (int)
warmup (int)
method observe(this, normResid)
Records one realized normalized residual (e.g. |close - center| / sigma).
Namespace types: Conformal
Parameters:
this (Conformal)
normResid (float)
method k(this, fallback)
The learned width multiplier: the target-quantile of observed residuals. Returns `fallback` until warm. Band = center ± k() * unitWidth delivers ~target coverage.
Namespace types: Conformal
Parameters:
this (Conformal)
fallback (float)
newBarrier()
Creates a barrier tracker for first-touch setup outcomes.
method arm(this, target, stop)
Arms a setup: which barrier must be touched first for a win (tgt) vs a loss (stp).
Namespace types: Barrier
Parameters:
this (Barrier)
target (float)
stop (float)
method check(this, barHigh, barLow)
Checks the current bar. Returns +1 (target first), -1 (stop first), 0 (still open). If both are inside one bar, the stop wins: conservative by design.
Namespace types: Barrier
Parameters:
this (Barrier)
barHigh (float)
barLow (float)
method winRate(this)
Lifetime win rate of resolved setups in percent, or na.
Namespace types: Barrier
Parameters:
this (Barrier)
score(pq, cA, cB, minN)
The composite 0-100 audit score. Calibration earns up to 50 points
(25 per coverage tracker; pass the same tracker twice if you only
have one band). Skill earns up to 50 (Brier skill vs coin flip).
Returns na until minN resolutions on the prequential tracker.
Parameters:
pq (Prequal)
cA (Coverage)
cB (Coverage)
minN (int)
panel(pq, cA, cB, pos)
Renders the Receipt, the standard audit panel. Pass na for trackers you don't use.
Parameters:
pq (Prequal)
cA (Coverage)
cB (Coverage)
pos (string)
Prequal
Fields:
pPend (series float)
accEma (series float)
brierEma (series float)
hits (series int)
n (series int)
emaA (series float)
Coverage
Fields:
target (series float)
loPend (series float)
hiPend (series float)
covEma (series float)
hits (series int)
n (series int)
emaA (series float)
Conformal
Fields:
resid (array)
target (series float)
win (series int)
warm (series int)
Barrier
Fields:
tgt (series float)
stp (series float)
live (series bool)
wins (series int)
losses (series int) Library

DivergenceLineLabelOutput_UtilitiesDivergenceLineLabelOutput_Utilities is a shared Pine v6 output library for scripts that already have their own oscillator, pivot, structure, or divergence logic but want a reusable divergence-rendering layer.
It centralizes the parts of the workflow that tend to get rewritten across oscillator scripts:
• HH / LH / HL / LL / EQ structure resolution
• regular / hidden divergence state checks
• same-structure context state checks
• divergence and context color routing
• standardized divergence/context label text
• price-pane and oscillator-pane line helpers
• price-pane and oscillator-pane label helpers
• confirmed-object slot visibility helpers
• live-preview line and label helpers
• native-pane and price-pane pivot context box helpers
On the example chart, the confirmed divergence lines, live preview lines, divergence labels, context labels, and pivot context boxes are all materially driven by this library.
This library is intentionally focused on output and object management. It does not calculate RSI, MACD, VFI, volume flow, pressure, or any other oscillator. It does not confirm pivots or decide which pivots are valid. Calling scripts remain responsible for their own oscillator engine, pivot engine, comparison logic, colors, visibility modes, and signal interpretation.
How to use
Import the library near the top of your script in global scope, alongside any other imports, before you start calling its helpers.
Typical placement:
//@version=6
indicator(...) or strategy(...)
import MYNAMEISBRANDON/DivergenceLineOutput_Utilities/1 as DivUtils
Replace /1 with the latest published version if a newer version is available.
This library expects the calling script to already know the price pivot, oscillator pivot, prior pivot reference, structure state, and output settings it wants to use. The library then handles the reusable line, label, live-preview, slot-budget, and box output layer.
➖Structure + Divergence State Helpers➖
These helpers convert pivot comparisons into structure tags and divergence/context states.
divStructure(curr, prev, isHigh)
Resolves HH, LH, HL, LL, or EQ from a current pivot and previous pivot.
Parameters:
curr (float): Current pivot value
prev (float): Previous pivot value
isHigh (bool): True for high-side comparison, false for low-side comparison
Returns:
Structure string
divState(priceStruct, oscStruct)
Resolves regular and hidden divergence states from price and oscillator structure tags.
Returns:
regularBear, regularBull, hiddenBear, hiddenBull, anyDivergence
divContextState(priceStruct, oscStruct)
Resolves same-structure context states from price and oscillator structure tags.
Returns:
highContinuation, highFade, lowContinuation, lowLift, anyContext
divColor(priceStruct, oscStruct, regularBearColor, regularBullColor, hiddenBearColor, hiddenBullColor, highContinuationColor, lowContinuationColor, highFadeColor, lowLiftColor, fallbackColor)
Routes a divergence or context pair to the matching caller-supplied color.
Returns:
Resolved color
➖Style + Label Helpers➖
These helpers keep divergence output styling consistent across scripts.
divLineStyle(styleIn)
Converts user-facing line-style text into Pine line-style enums.
Parameters:
styleIn (simple string): Solid, Dashed, or Dotted
Returns:
Pine line style
divLabelSize(sizeIn)
Converts user-facing label-size text into Pine label-size enums.
Parameters:
sizeIn (simple string): Tiny, Small, Normal, Large, or Huge
Returns:
Pine label size
divContrastText(bg)
Chooses black or white text based on background brightness.
Parameters:
bg (color): Background color
Returns:
Readable contrast text color
divLabelText(divType, divSide, priceStruct, oscStruct, formatMode)
Builds standardized divergence label text.
Parameters:
divType (simple string): Usually Reg or Hid
divSide (simple string): Usually Bull or Bear
priceStruct (string): Price structure tag
oscStruct (string): Oscillator structure tag
formatMode (simple string): Full, No Prefix, or Type Only
Returns:
Formatted label text
➖Confirmed Line Helpers➖
These helpers create and manage confirmed divergence or context lines.
clearLines(lines, colors)
Deletes all lines in an array and clears the matching color array.
pushOscLine(lines, colors, show, active, x1, y1, x2, y2, lineColor, lineTransp, lineWidth, lineStyle, maxLines)
Pushes a confirmed oscillator-pane line into line/color arrays.
pushPriceLine(lines, colors, show, active, x1, y1, x2, y2, lineColor, lineTransp, lineWidth, lineStyle, maxLines)
Pushes a confirmed price-pane line into line/color arrays using force_overlay=true.
Note:
Price helpers draw on the main chart from overlay=false oscillator scripts. Oscillator helpers draw in the script’s native pane.
➖Confirmed Label Helpers➖
These helpers create and manage confirmed divergence or context labels.
clearLabels(labels, colors)
Deletes all labels in an array and clears the matching color array.
pushOscLabel(labels, colors, show, active, xIndex, y, labelText, styleText, lineColor, labelTextOnly, labelBgTransp, labelSize, maxLabels)
Pushes a confirmed oscillator-pane label into label/color arrays.
pushPriceLabel(labels, colors, show, active, xTime, labelText, ylocText, styleText, lineColor, labelTextOnly, labelBgTransp, labelSize, maxLabels)
Pushes a confirmed price-pane label into label/color arrays using force_overlay=true.
Note:
For price-pane labels, xTime uses bar time and ylocText controls whether the label appears above or below the price bar.
➖Confirmed Slot Visibility Helpers➖
These helpers allow scripts to keep confirmed lines and labels stored while only showing the most recent visible slots.
applyLineSlots(lines, colors, visibleSlots)
Applies a visible slot budget to confirmed line arrays without deleting older objects.
applyLabelSlots(labels, colors, visibleSlots, labelTextOnly, labelBgTransp)
Applies a visible slot budget to confirmed label arrays without deleting older objects.
Example:
• Max Regular Lines = 1
- Live regular active = live regular only
- No live regular = latest confirmed regular only
• Max Regular Lines = 2
- Live regular active = live regular + latest confirmed regular
- No live regular = latest two confirmed regular lines
➖Live Preview Line Helpers➖
These helpers create, update, or delete live divergence preview lines.
syncLiveOscLine(ln, show, x1, y1, x2, y2, lineColor, lineWidth, lineStyle)
Creates, updates, or deletes a live oscillator-pane line.
syncLivePriceLine(ln, show, x1, y1, x2, y2, lineColor, lineWidth, lineStyle)
Creates, updates, or deletes a live price-pane line using force_overlay=true.
➖Live Preview Label Helpers➖
These helpers create, update, or delete live divergence preview labels.
syncLiveOscLabel(lbl, show, xIndex, y, labelText, styleText, lineColor, labelTextOnly, labelBgTransp, labelSize)
Creates, updates, or deletes a live oscillator-pane label.
syncLivePriceLabel(lbl, show, xTime, labelText, ylocText, styleText, lineColor, labelTextOnly, labelBgTransp, labelSize)
Creates, updates, or deletes a live price-pane label using force_overlay=true.
➖Pivot Context Box Helpers➖
These helpers provide lightweight box utilities for scripts that want to frame confirmed pivot zones.
divPivotBoxBounds(pivotValue, innerPct)
Resolves a thin box around a pivot value using an inner percentage.
Returns:
top, bottom, ok
syncNativeBox(bx, show, left, right, top, bottom, fillColor, borderColor, borderStyle, borderWidth)
Creates, updates, or deletes a native-pane pivot context box.
syncPriceBox(bx, show, left, right, top, bottom, fillColor, borderColor, borderStyle, borderWidth)
Creates, updates, or deletes a price-pane pivot context box using force_overlay=true.
➖Divergence Model➖
Regular Bearish Divergence:
Price makes HH while oscillator makes LH.
Regular Bullish Divergence:
Price makes LL while oscillator makes HL.
Hidden Bearish Divergence:
Price makes LH while oscillator makes HH.
Hidden Bullish Divergence:
Price makes HL while oscillator makes LL.
➖Structure Context Model➖
High-side continuation:
Price makes HH while oscillator also makes HH.
High-side fading:
Price makes LH while oscillator also makes LH.
Low-side continuation:
Price makes LL while oscillator also makes LL.
Low-side lifting:
Price makes HL while oscillator also makes HL.
Structure context is not divergence. It shows same-structure agreement between price and oscillator.
➖Important Notes➖
This library is an output utility layer only.
It does not:
• calculate an oscillator
• confirm pivots
• choose pivot anchors
• decide whether a divergence is valid
• decide trade direction
• decide final signal logic
Calling scripts remain responsible for:
• oscillator calculation
• pivot confirmation
• price/oscillator comparison logic
• visibility settings
• color choices
• max-line and max-label budgets
• final visual interpretation
For overlay=false oscillator scripts, Price + Oscillator / Price Only / Oscillator Only / Hide output modes work well.
For overlay=true price-pane scripts, Price Only / Hide output modes usually make the most sense. 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

ExprLibExprLib is a library for parsing and evaluating string expressions. It allows scripts to expose configurable logic by letting users define custom conditions and calculations based on available data.
█ KEY FEATURES
• Rich expression support:
• Built-in constants (e.g., `10`, `2.5`, `5e-2`, `true`, `false`, `na`)
• Custom constants
• Variables
• Arithmetic operators: `+`, `-`, `*`, `/`, `%`
• Comparison operators: `>`, `<`, `>=`, `<=`, `==`, `!=`
• Logical operators: `AND`, `OR`, `NOT` (with aliases)
• Ternary operator: `condition ? if_true : if_false`
• Parentheses: `(`, `)`
• Built-in functions: `na()`, `nz()`, `max()`, `pow()`, `sqrt()`, `random()`, and more!
• Graceful error handling during parsing and evaluation
• Optimized for evaluation performance (RPN-based approach)
█ NOTE
Since the library description cannot be changed or removed after publication, some information here may be outdated. However, you can always get the latest version of the documentation at the bottom of the source code.
█ QUICK START
An example of an indicator that colors areas on a chart where the expression evaluates to `true`:
//@version=6
indicator("Quick Start", overlay = true)
import A1trdX/ExprLib/1 as ExprLib
// ---------------
// INPUTS
// ---------------
// Let the user customize the expression
inputExpressionStr = input.text_area("trend_up AND (rsi < 50 OR close < open)", "Expression")
// -------------------
// CALCULATION
// -------------------
// Prepare some data to use in the expression.
rsi = ta.rsi(close, 14)
ema = ta.ema(close, 200)
isTrendUp = close > ema
isTrendDown = close < ema
// Step 0: Prepare the parser and evaluator.
var parser = ExprLib.createExpressionParser()
var evaluator = ExprLib.createExpressionEvaluator()
// Step 1: Parse the expression string.
var expression = parser.parse(inputExpressionStr)
// Step 2 (Recommended): Verify whether the expression was parsed without errors.
if not parser.isParsed
// You can define your own logic to handle errors
runtime.error("Failed to parse expression: " + parser.error.message)
// Step 3: Assign values to variables. Both numbers and booleans are supported.
expression.setVariable("open", open)
expression.setVariable("close", close)
expression.setVariable("rsi", rsi)
expression.setVariable("trend_up", isTrendUp)
expression.setVariable("trend_down", isTrendDown)
// Step 4: Evaluate the expression.
bool result = evaluator.evaluateToBool(expression)
// Step 4 (Alternative): If you expect a numeric result, use `evaluate()` instead.
// float result = evaluator.evaluate(expression)
// Step 5 (Recommended): Verify whether the expression was evaluated without errors.
if not evaluator.isEvaluated
// You can define your own logic to handle errors
runtime.error("Failed to evaluate expression: " + evaluator.error.message)
// ----------------
// GRAPHICS
// ----------------
// Highlight bars where the expression returns `true`
bgcolor(result ? color.new(color.green, 90) : na)
█ EXPRESSION SYNTAX REFERENCE
❱❱ Components
An expression can include:
• Constants
• Variables
• Operators
• Functions
• Parentheses
• Spaces, tabs, or newlines
❱❱ Data Types
Constants and variables can have the following data types:
• Numeric (`int`, `float`)
• Boolean (`bool`)
• Undefined (`na`)
❱❱ Identifiers
Identifiers are names used to refer to named constants, variables, and functions.
Identifier naming rules:
• Must start with a letter (`a-z`, `A-Z`) or underscore (`_`).
• May contain letters (`a-z`, `A-Z`), digits (`0-9`), and underscores (`_`).
Identifiers cannot contain spaces or other characters.
Identifiers are case-sensitive.
❱❱ Constants
Numeric Constants
Examples:
+-----------+--------------+
| Constant | Plain Value |
+-----------+--------------+
| 12 | 12.00 |
| 0.05 | 0.05 |
| .05 | 0.05 |
| 5e-2 | 0.05 |
| 5E-2 | 0.05 |
| 1.2e4 | 12000.00 |
+-----------+--------------+
Named Constants
Available built-in named constants:
+----------+-------------------------------------+-------------------------+
| Name | Description | Pine Script Equivalent |
+----------+-------------------------------------+-------------------------+
| `true` | Boolean TRUE | `true` |
| `false` | Boolean FALSE | `false` |
| `na` | Undefined value | `na` |
| `pi` | Pi (~3.14159) | `math.pi` |
| `e` | Euler's number (~2.71828) | `math.e` |
| `phi` | Golden ratio (~1.61803) | `math.phi` |
| `rphi` | Golden ratio conjugate (~0.61803) | `math.rphi` |
+----------+-------------------------------------+-------------------------+
It is possible to add custom constants.
❱❱ Variables
It is possible to add variables, just like custom constants, except that variable values can be changed before each evaluation.
❱❱ Operators
The following operators are supported:
+--------------+-------------+-------------------------+-------------+------------------+-------------+
| Type | Operator | Name | Aliases | Example #1 | Example #2 |
+--------------+-------------+-------------------------+-------------+------------------+-------------+
| Arithmetic | `+` | Add | | `a + b` | |
| Arithmetic | `-` | Subtract | | `a - b` | |
| Arithmetic | `*` | Multiply | | `a * b` | |
| Arithmetic | `/` | Divide | | `a / b` | |
| Arithmetic | `%` | Modulo | | `a % b` | |
| Comparison | `>` | Greater than | | `a > b` | |
| Comparison | `<` | Less than | | `a < b` | |
| Comparison | `>=` | Greater than or equal | | `a >= b` | |
| Comparison | `<=` | Less than or equal | | `a <= b` | |
| Comparison | `==` | Equal | | `a == b` | |
| Comparison | `!=` | Not equal | | `a != b` | |
| Logical | `AND` | Logical AND | `&&`, `&` | `a AND b` | `a && b` |
| Logical | `OR` | Logical OR | `||`, `|` | `a OR b` | `a || b` |
| Logical | `NOT` | Logical NOT | `!` | `NOT x` | `!x` |
| Conditional | `?:` | Ternary | | `cond ? x : y` | |
| Unary | Unary `+` | Unary plus | | `+x` | |
| Unary | Unary `-` | Unary minus | | `-x` | |
+--------------+-------------+-------------------------+-------------+------------------+-------------+
Logical operator names are case-insensitive.
Operator precedence:
+------------+-----------------------------+
| Precedence | Operators |
+------------+-----------------------------+
| 8 | Unary `-`, Unary `+`, `NOT` |
| 7 | `*`, `/`, `%` |
| 6 | `+`, `-` |
| 5 | `>`, `<`, `>=`, `<=` |
| 4 | `==`, `!=` |
| 3 | `AND` |
| 2 | `OR` |
| 1 | `?:` |
+------------+-----------------------------+
Operator associativity:
• Unary `+`, Unary `-`, `NOT`, and ternary are right-associative
• Other operators are left-associative
❱❱ Parentheses
Parentheses are used to group sub-expressions and override the default operator precedence.
Example:
((a + b) * c + 1) * d
❱❱ Functions
Functions are called by an identifier followed immediately by parentheses: `func(arg1, arg2)`.
Arguments are separated by commas. Each argument can be any valid expression, including another function call.
Available built-in functions:
+-------------------------------+----------+------------------------------------------------------------------------+
| Function | Args | Description |
+-------------------------------+----------+------------------------------------------------------------------------+
| `na(x)` | 1 | Returns `true` when `x` is `na`, `false` otherwise. |
| `nz(x, fallback)` | 2 | Returns `x` when it is not `na`, `fallback` otherwise. |
| `max(x1, x2, ...)` | 2..999 | Returns the largest argument. |
| `min(x1, x2, ...)` | 2..999 | Returns the smallest argument. |
| `pow(base, exponent)` | 2 | Returns `base` raised to `exponent`. |
| `sqrt(x)` | 1 | Returns the square root of `x`. |
| `clamp(x, min, max)` | 3 | Restricts `x` to the ` ` range. |
| `abs(x)` | 1 | Returns the absolute value of `x`. |
| `ceil(x)` | 1 | Rounds `x` up to the nearest integer. |
| `floor(x)` | 1 | Rounds `x` down to the nearest integer. |
| `round(x)` | 1 | Rounds `x` to the nearest integer. |
| `round_to_mintick(x)` | 1 | Rounds `x` to the symbol's minimum tick precision. |
| `log(x)` | 1 | Returns the natural logarithm of `x`. |
| `log10(x)` | 1 | Returns the base-10 logarithm of `x`. |
| `sign(x)` | 1 | Returns the sign of `x`: `1`, `0`, or `-1`. |
| `cos(x)` | 1 | Returns the cosine of `x` in radians. |
| `sin(x)` | 1 | Returns the sine of `x` in radians. |
| `tan(x)` | 1 | Returns the tangent of `x` in radians. |
| `acos(x)` | 1 | Returns the arccosine of `x` in radians. |
| `asin(x)` | 1 | Returns the arcsine of `x` in radians. |
| `atan(x)` | 1 | Returns the arctangent of `x` in radians. |
| `deg(x)` | 1 | Converts radians to degrees. |
| `rad(x)` | 1 | Converts degrees to radians. |
| `random(min, max, seed)` | 0..3 | Returns a random float. Bounds default to 0 and 1. Seed is optional. |
| `random_int(min, max, seed)` | 2..3 | Returns a random integer. Seed is optional. |
| `random_bool(seed)` | 0..1 | Returns a random boolean value. Seed is optional. |
+-------------------------------+----------+------------------------------------------------------------------------+
The number of arguments can be either fixed or variable.
For example, the `max(x1, x2, ...)` function supports 2 to 999 arguments, so the following calls to this function are valid:
max(x1, x2)
max(x1, x2, x3)
max(x1, x2, x3, x4, x5)
Other functions may have optional arguments. For example, the following calls to the `random(min, max, seed)` function are valid:
random() // Random float from 0 to 1
random(0.5) // Random float from 0.5 to 1
random(0.5, 2) // Random float from 0.5 to 2
random(0.5, 2, 777) // Random float from 0.5 to 2 with a specific seed
❱❱ Whitespace
Spaces, tabs, and line breaks are ignored between symbols. For example, an expression can be formatted across multiple lines:
price > ema_slow
AND ema_fast > ema_slow
AND (bb_lo_up OR rsi_lo_up)
█ PARSING
❱❱ Workflow
Before evaluating an expression, it must be parsed. To do this:
• Create a parser in advance using the `createExpressionParser()` function.
• Call the `parse()` method, passing the expression string as an argument.
Example:
var parser = ExprLib.createExpressionParser()
var expr1 = parser.parse("a + 2")
var expr2 = parser.parse("a + b * c")
❱❱ Error Handling
A user may enter an invalid expression. In this case, the parser will return `na` instead of a valid expression object. The parser stores the result of the last parse. You can use that result to retrieve the status and error information.
Parser and error field structures:
type ExpressionParser
bool isParsed // `true` if the last parse completed successfully, `false` otherwise.
ParseError error // Error from the last parse attempt. If the last parse was successful, then this field is `na`.
type ParseError
string message // Error message.
int index // Character index where the parser detected the error.
For example, suppose we want to display an error message on the chart if one of the expressions is invalid:
//@version=6
indicator("Parser Error Handling")
import A1trdX/ExprLib/1 as ExprLib
inputExpr1 = input.text_area("a + 2", "Expression 1")
inputExpr2 = input.text_area("a + b * c /", "Expression 2")
displayErrorMessage(string errorMessage) =>
var table errorMessageTable = na
if na(errorMessageTable)
errorMessageTable := table.new(position.top_right, 1, 1)
errorMessageTable.cell(0, 0, errorMessage,
bgcolor = color.red,
text_color = color.white,
text_halign = text.align_left,
text_formatting = text.format_bold)
checkParsed(ExprLib.ExpressionParser parser, string prefix) =>
if not parser.isParsed
displayErrorMessage(prefix + parser.error.message)
var parser = ExprLib.createExpressionParser()
var expr1 = parser.parse(inputExpr1)
checkParsed(parser, "Failed to parse expression #1: ")
var expr2 = parser.parse(inputExpr2)
checkParsed(parser, "Failed to parse expression #2: ")
A blank expression (e.g., "") is allowed and will evaluate to `na` (or `false` when returning a boolean value).
❱❱ Custom Constants
You can add your own named constants during the parsing stage. To do this:
• Create a constant pool in advance using the `createConstantPool()` function.
• Set constants and their values using the `set()` method.
• Pass the constant pool to the `parse()` method.
Example:
var constantPool = ExprLib.createConstantPool()
if barstate.isfirst
constantPool.set("one", 1)
constantPool.set("two", 2)
constantPool.set("three_p_one", 3.1)
constantPool.set("yes", true)
constantPool.set("no", false)
var parser = ExprLib.createExpressionParser()
var expr = parser.parse("one + two", constantPool)
The `set()` method returns the same constant pool object, so you can chain calls together. This is more convenient and more elegant:
var constantPool = ExprLib.createConstantPool()
.set("one", 1)
.set("two", 2)
.set("three_p_one", 3.1)
.set("yes", true)
.set("no", false) // Note that the indentation is 7 spaces (not a multiple of 4)
var parser = ExprLib.createExpressionParser()
var expr = parser.parse("one + two", constantPool)
You can also override built-in constants:
var constantPool = ExprLib.createConstantPool()
.set("true", false)
.set("false", -1)
.set("na", 0.0)
█ EVALUATION
❱❱ Type Coercion
An expression can consist of values of different data types. ExprLib does not have strict data type checking. Instead, all values are converted to `float` and then back if necessary.
Converting `bool` to `float`:
• `true` -> `1.0`
• `false` -> `0.0`
Converting `float` to `bool`:
• `0.0` or `na` -> `false`
• Any other value -> `true`
Thus, expressions that incorrectly combine different data types are allowed. For example, `true + 2` will return `3.0`. Strict typing requires additional memory as well as additional computational resources during evaluation, which is a critical concern. Therefore, it was decided not to implement it.
As in Pine Script, most operations with an `na` operand results in `na` or `false`, but logical operations first convert `na` to `false`, so their result follows boolean logic. For example:
• `3 - na` returns `na`
• `3 > na` returns `false`
• `3 <= na` also returns `false`
• `na AND true` returns `false`
• `na OR true` returns `true`
• `NOT na` returns `true`
❱❱ Workflow
To evaluate an expression:
• Create an evaluator in advance using the `createExpressionEvaluator()` function.
• Set variables and their values in the expression using the `setVariable()` method.
• Call the `evaluate()` or `evaluateToBool()` method, passing the expression as an argument.
The `evaluate()` and `evaluateToBool()` methods differ in their return types. The former returns a `float` result, while the latter returns a `bool` result. The method to call depends on the expected result type.
Example:
// Parsed expressions:
// - expr1 <= "(H - L) / 2 + L"
// - expr2 <= "rsi_oversold AND close > open"
// Initialize evaluator
var evaluator = ExprLib.createExpressionEvaluator()
// Set variables and evaluate the first expression
expr1.setVariable("H", high)
expr1.setVariable("L", low)
float result1 = evaluator.evaluate(expr1)
// Set variables and evaluate the second expression
rsi = ta.rsi(close, 14)
expr2.setVariable("open", open)
expr2.setVariable("close", close)
expr2.setVariable("rsi_oversold", rsi < 30)
expr2.setVariable("rsi_overbought", rsi > 70)
bool result2 = evaluator.evaluateToBool(expr2)
❱❱ Variables
If an expression contains an identifier that is neither a function nor a constant, and this identifier has not been assigned a variable value, then this identifier is considered a constant with the value `na` (or `false` in boolean operations).
The `setVariable()` method overrides existing constants (both built-in and custom). For example, by default, the identifier `e` is used as the constant Euler's number (~2.71828). However, you can make `e` your own variable:
// Parsed expressions:
// - expr <= "e + 1"
expr.setVariable("e", 5) // Now `e` is equal to `5` instead of `2.7182818284590452`
result = evaluator.evaluate(expr) // `6.0`
The `setVariable()` method does not need to be called on each bar if the variable's value does not change. The expression always stores and uses the last value set.
You can clear all previously set variables using the `clearVariables()` method. This can be useful if you have many variables and want to reset them all and set values for only a small subset.
❱❱ Error Handling
In some cases (for example, when dividing by zero), evaluation results in an error. In this case, `evaluate()` will return `na`, and `evaluateToBool()` will return `false`. Like the parser, the evaluator stores the result of the last evaluation.
Evaluator and error field structures:
type ExpressionEvaluator
bool isEvaluated // `true` if the last evaluation completed successfully, `false` otherwise.
EvaluationError error // Error from the last evaluation attempt. If the last evaluation was successful, then this field is `na`.
type EvaluationError
EvaluationErrorReason reason // Error reason.
string message // Error message.
enum EvaluationErrorReason
DIVISION_BY_ZERO
Example:
//@version=6
indicator("Evaluator Error Handling")
import A1trdX/ExprLib/1 as ExprLib
inputExpr1 = input.text_area("a + 2", "Expression 1")
inputExpr2 = input.text_area("a + b / c", "Expression 2")
displayErrorMessage(string errorMessage) =>
var table errorMessageTable = na
if na(errorMessageTable)
errorMessageTable := table.new(position.top_right, 1, 1)
errorMessageTable.cell(0, 0, errorMessage,
bgcolor = color.red,
text_color = color.white,
text_halign = text.align_left,
text_formatting = text.format_bold)
// Parse
checkParsed(ExprLib.ExpressionParser parser, string prefix) =>
if not parser.isParsed
displayErrorMessage(prefix + parser.error.message)
var parser = ExprLib.createExpressionParser()
var expr1 = parser.parse(inputExpr1)
checkParsed(parser, "Failed to parse expression #1: ")
var expr2 = parser.parse(inputExpr2)
checkParsed(parser, "Failed to parse expression #2: ")
// Evaluate
checkEvaluated(ExprLib.ExpressionEvaluator evaluator, string prefix) =>
if not evaluator.isEvaluated
displayErrorMessage(prefix + evaluator.error.message)
var evaluator = ExprLib.createExpressionEvaluator()
expr1.setVariable("a", open)
expr1.setVariable("b", close)
expr1.setVariable("c", 0)
result1 = evaluator.evaluate(expr1)
checkEvaluated(evaluator, "Failed to evaluate expression #1: ")
expr2.setVariable("a", open)
expr2.setVariable("b", close)
expr2.setVariable("c", 0)
result2 = evaluator.evaluate(expr2)
checkEvaluated(evaluator, "Failed to evaluate expression #2: ")
Currently, the only possible cause of this error is division by zero. You can disable this error and have the evaluator interpret the result of division by zero as `na`. To do this, disable the corresponding flag in the evaluator:
evaluator.setFailOnDivisionByZero(false)
Thus, an expression like `na(5 / 0) ? 1 : 2` will return `1` instead of an error.
█ BEST PRACTICES
• Reuse `ExpressionParser` and `ExpressionEvaluator` objects whenever possible.
• Parse expressions only once, and evaluate them as needed. Parsing is slow. Evaluation is fast.
• If certain variable values change rarely, call `setVariable()` only when necessary.
• Try to avoid excessive numbers of variables whose values change frequently. This can impact performance even if they're not used in the expression.
█ API REFERENCE
❱❱ Expression Parser
ExpressionParser
Expression parser.
Fields:
isParsed (series bool) : `true` if the last parse completed successfully, `false` otherwise.
error (ParseError) : Error from the last parse attempt. If the last parse was successful, then this field is `na`.
createExpressionParser()
Creates an expression parser.
Returns: Expression parser.
method parse(parser, exprStr, constantPool)
Parses an expression.
Namespace types: ExpressionParser
Parameters:
parser (ExpressionParser) : Expression parser.
exprStr (string) : Expression string. Can be empty, blank, or 'na'. That way expression is valid and will return `na` on evaluation.
constantPool (ExpressionConstantPool) : (Optional) Named constants.
Returns: Parsed expression. If an error occurs during parsing, then the returned expression will be `na`.
You can check validity and error details accessing parser's `isParsed` and `error` fields.
❱❱ Expression
Expression
Parsed expression.
method setVariable(expr, identifier, value)
Assigns a numeric value to a variable.
Namespace types: Expression
Parameters:
expr (Expression) : Expression.
identifier (string) : Variable name.
value (float) : Value.
Returns: This expression.
method setVariable(expr, identifier, value)
Assigns a boolean value to a variable.
Namespace types: Expression
Parameters:
expr (Expression) : Expression.
identifier (string) : Variable name.
value (bool) : Value.
Returns: This expression.
method clearVariables(expr)
Clears all variable values.
Namespace types: Expression
Parameters:
expr (Expression) : Expression.
Returns: This expression.
❱❱ Constant Pool
ExpressionConstantPool
Expression constant pool.
createConstantPool()
Creates an expression constant pool.
Returns: Expression constant pool.
method set(pool, identifier, value)
Assigns a numeric constant value.
Namespace types: ExpressionConstantPool
Parameters:
pool (ExpressionConstantPool) : Expression constant pool.
identifier (string) : Constant name.
value (float) : Value.
Returns: This expression constant pool.
method set(pool, identifier, value)
Assigns a boolean constant value.
Namespace types: ExpressionConstantPool
Parameters:
pool (ExpressionConstantPool) : Expression constant pool.
identifier (string) : Constant name.
value (bool) : Value.
Returns: This expression constant pool.
method clear(pool)
Clears all constants.
Namespace types: ExpressionConstantPool
Parameters:
pool (ExpressionConstantPool) : Expression constant pool.
Returns: This expression constant pool.
❱❱ Expression Evaluator
ExpressionEvaluator
Expression evaluator.
Fields:
isEvaluated (series bool) : `true` if the last evaluation completed successfully, `false` otherwise.
error (EvaluationError) : Error from the last evaluation attempt. If the last evaluation was successful, then this field is `na`.
result (series float) : Numeric result of the last evaluation.
boolResult (series bool) : Boolean result of the last evaluation.
createExpressionEvaluator()
Creates an expression evaluator.
Returns: Expression evaluator.
method evaluate(evaluator, expr)
Evaluates an expression.
Namespace types: ExpressionEvaluator
Parameters:
evaluator (ExpressionEvaluator) : Expression evaluator.
expr (Expression) : Expression to evaluate.
Returns: Numeric evaluation result.
For boolean-result expressions `1.0` means `true` and `0.0` means `false`.
Returns `na` if expression is empty.
method evaluateToBool(evaluator, expr)
Evaluates an expression.
Namespace types: ExpressionEvaluator
Parameters:
evaluator (ExpressionEvaluator) : Expression evaluator.
expr (Expression) : Expression to evaluate.
Returns: Boolean evaluation result.
Returns `false` if expression is empty.
method setFailOnDivisionByZero(evaluator, value)
Sets whether division or modulo by zero should fail evaluation.
Namespace types: ExpressionEvaluator
Parameters:
evaluator (ExpressionEvaluator) : Expression evaluator.
value (bool) : If `true`, division or modulo by zero fails evaluation. If `false`, it produces `na`.
Returns: This expression evaluator.
❱❱ Errors
ParseError
Error that occurred during expression parsing.
Fields:
message (series string) : Error message.
index (series int) : Character index where the parser detected the error.
EvaluationError
Error that occurred during expression evaluation.
Fields:
reason (series EvaluationErrorReason) : Error reason.
message (series string) : Error message. Library

Library

CyberMarketLib# CyberMarketLib v2
CyberMarketLib provides market structure analysis combining swing point detection, Break of Structure (BoS) / Change of Character (CHoCH) identification, session classification, and volatility regime tracking.
## What it does
Delivers four core capabilities: swing point tracking (configurable left/right bar lookback), market structure events (BoS/CHoCH for trend continuation vs reversal), session classification (Asia/London/NY via UTC bucketing), and volatility regimes (LOW/NORMAL/HIGH/EXTREME via ATR percentiles). Build context-aware indicators that adapt to market conditions.
Outputs FractalData structs, StructureEvent/Session/VolRegime enums. All pivots use confirmed swing points (requires right_len bars validation), preventing repainting.
## How it works
Swing detection: `high < high > high `. Stores pivots in SwingHistory circular buffers with automatic capacity management.
BoS/CHoCH follows Smart Money Concepts:
- BOS_UP/DOWN: Price breaks recent swing (trend continuation)
- CHOCH_UP/DOWN: Pivot break after opposite swing (reversal)
Sessions via UTC hours: ASIA (00-08), LONDON (08-13), NY_OVERLAP (13-17), NY_AFTERNOON (17-21), OFF_HOURS (21-24).
Volatility regimes via ATR percentiles (100-bar window): LOW (<25th), NORMAL (25-75th), HIGH (75-90th), EXTREME (>90th).
## Why this is original
Only PulseWire library combining BoS/CHoCH, sessions, and volatility regimes. Existing SMC indicators lack reusable libraries.
Unique features:
- Confirmed pivots only (no repainting)
- CHoCH sequence analysis (pivot pattern detection)
- UTC-based sessions (exchange-agnostic, DST-safe)
- Percentile volatility (asset-adaptive)
- Circular buffer (O(1) operations, memory-efficient)
Designed for composability: sessions → conditional logic, regimes → stop multipliers, BoS/CHoCH → entry/exit signals.
## How to use it
```pine
//@version=6
indicator("CyberMarketLib Demo", overlay=true)
import cybermediaboy/CyberMarketLib/2 as ML
// Swing points + BoS/CHoCH detection
var swing_hist = ML.f_swing_history_new(max_n=20)
var fractal = ML.f_detect_pivot(left_len=5, right_len=5)
if not na(fractal)
swing_hist.push(fractal)
var event = ML.f_detect_structure_event(swing_hist, close)
// event: BOS_UP, BOS_DOWN, CHOCH_UP, CHOCH_DOWN, NONE
// Session + volatility regime
session = ML.f_current_session() // ASIA, LONDON, NY_OVERLAP, etc.
vol_regime = ML.f_volatility_regime(14, 100) // LOW, NORMAL, HIGH, EXTREME
// Adaptive stops
atr = ta.atr(14)
stop_mult = vol_regime == ML.VolRegime.EXTREME ? 3.0 : 1.5
plot(close - atr * stop_mult, "Stop", color.red)
```
## Key functions
- `f_detect_pivot()` - Confirmed swing points (no repainting)
- `f_detect_structure_event()` - BoS/CHoCH detection
- `f_current_session()` - UTC-based session classification
- `f_volatility_regime()` - ATR percentile regimes
- `f_htf_for()` - Higher timeframe string generation
- SwingHistory UDT - Circular buffer for pivot storage
## Limitations
- Swing detection: `right_len` bars confirmation delay (lag vs repainting indicators)
- BoS/CHoCH: Assumes trending markets (false signals in choppy ranges)
- Sessions: UTC-only (no exchange-native or DST-aware sessions)
- Volatility: ATR-based only (may lag on sudden spikes)
- SwingHistory: Fixed capacity at initialization
- CHoCH: Requires manual state tracking to avoid duplicate signals
Library

CyberSignalLib# CyberSignalLib v2
CyberSignalLib provides advanced signal processing tools for Pine Script traders, combining Kalman filtering, entropy-based changepoint detection, and market microstructure analysis in a single dependency.
## What it does
SignalLib delivers three core capabilities: N-dimensional Kalman filters for multi-feature state estimation (price, velocity, z-scores), entropy-based changepoint detectors for regime shifts (NIS, CUSUM, BOCPD), and microstructure metrics for order flow analysis (delta, aggression, volume imbalance). Traders use these tools to build adaptive indicators that respond to market regime changes—for example, a Kalman filter tracking price and volatility simultaneously, with automatic parameter adjustment when a changepoint detector signals a structural break.
The library outputs filtered state estimates (smoothed price, velocity, Mahalanobis distance), changepoint probabilities (0-1 scores indicating regime shift likelihood), and microstructure features (signed delta, aggression ratio, volume-weighted imbalance). All functions support real-time bar-by-bar updates with minimal memory overhead via circular buffers and packed covariance matrices.
## How it works
The Kalman filter implementation uses an N-dimensional state vector with upper-triangular packed covariance storage, reducing memory from O(N²) to O(N(N+1)/2). The filter supports diagonal process noise (Q) and scalar measurement noise (R), both adaptive via innovation tracking. The update step follows the standard predict-correct cycle: predict state using transition matrix F, compute innovation (measurement - prediction), update state and covariance via Kalman gain. Normalized Innovation Squared (NIS) is computed as `innovation² / (H·P·H' + R)` to detect outliers and trigger adaptive R adjustments.
Changepoint detection uses three methods:
1. **NIS-based**: Flags regime change when NIS exceeds a threshold (e.g., 9.0 for 99% confidence under chi-squared distribution)
2. **CUSUM**: Cumulative sum of log-likelihood ratios, resets when crossing upper/lower bounds
3. **BOCPD (Bayesian Online Changepoint Detection)**: Maintains run-length distribution, computes changepoint probability via hazard function
Entropy calculations support four modes: binary (up/down), ternary (up/flat/down), combo (binary + ternary), and composite (weighted average). Shannon entropy is computed as `-Σ p_i log₂(p_i)` where p_i are empirical frequencies over a rolling window. High entropy (near maximum) indicates unpredictable price action; low entropy signals trending or mean-reverting regimes.
Microstructure metrics derive from tick-level order flow:
- **Delta**: Signed volume (buy volume - sell volume)
- **Aggression**: Ratio of aggressive orders (market orders) to total volume
- **Imbalance**: `(buy_vol - sell_vol) / (buy_vol + sell_vol)`, range
These metrics are computed via request.security calls to lower timeframes (1-minute typical) and aggregated to the chart timeframe.
## Why this is original
CyberSignalLib is the only PulseWire library combining Kalman filtering, changepoint detection, and microstructure analysis in a unified interface. Existing Kalman filter libraries are limited to 1D or 2D state spaces and lack adaptive noise parameters. No public library offers BOCPD or CUSUM changepoint detection. Microstructure metrics typically require manual request.security calls with hardcoded timeframes—SignalLib abstracts this into reusable functions with configurable lookback windows.
Unique features:
- **Tri-packed covariance**: Memory-efficient N-dimensional Kalman filter (supports up to 16 features on PulseWire's memory limits)
- **Adaptive Q/R**: Automatic process/measurement noise tuning based on innovation statistics, eliminating manual parameter tweaking
- **Trajectory store**: Circular buffer for Kalman state history, enabling lookback analysis (e.g., "was price above Kalman estimate 5 bars ago?")
- **Mahalanobis distance**: 3D analytic formula with shrinkage regularization for outlier detection in multi-feature space
- **Unified changepoint API**: Single enum-based interface for NIS/CUSUM/BOCPD, simplifying regime-switching indicator logic
No other Pine library provides this combination of statistical rigor (Kalman optimality, Bayesian changepoint inference) and practical usability (adaptive parameters, memory-efficient storage, microstructure integration).
## How to use it
```pine
//@version=6
indicator("CyberSignalLib Demo", overlay=true)
import cybermediaboy/CyberSignalLib/2 as SL
import cybermediaboy/NumLib/5 as N
// Example 1: 2D Kalman filter (price + velocity)
var kal = SL.f_kalman_init(nfeat=2, P0=1.0, Q0=0.01, R0=0.1, innov_window=20)
if not na(close)
kal.update_scalar(0, close, 1.0) // Measure price (feature 0)
kal.predict(SL.f_transition_identity(2))
kal.adapt_Q(Q_min=0.001, Q_max=0.1, gain=1.5)
kal.adapt_R(high_thresh=9.0, low_thresh=1.0, R_step=0.1)
float price_est = array.get(kal.x, 0)
float velocity_est = array.get(kal.x, 1)
plot(price_est, "Kalman Price", color.blue, linewidth=2)
plot(close + velocity_est * 10, "Velocity Offset", color.orange)
// Example 2: NIS-based changepoint detection
bool changepoint = kal.lastnis > 9.0 // 99% confidence threshold
bgcolor(changepoint ? color.new(color.red, 80) : na, title="Regime Change")
// Example 3: Entropy calculation (ternary mode)
var ent_buf = array.new(50, 0)
int direction = close > close ? 1 : (close < close ? -1 : 0)
array.push(ent_buf, direction)
if array.size(ent_buf) > 50
array.shift(ent_buf)
float entropy = SL.f_entropy_ternary(ent_buf)
plot(entropy, "Ternary Entropy", color.green)
// Example 4: Mahalanobis distance (3D outlier detection)
var z_vec = array.from(close, volume, ta.rsi(close, 14))
var mu_vec = array.from(ta.sma(close, 50), ta.sma(volume, 50), 50.0)
var cov_tri = array.from(1.0, 0.0, 0.0, 1.0, 0.0, 1.0) // Identity covariance
float maha = SL.f_mahalanobis_3d(z_vec, mu_vec, cov_tri, shrinkage=0.1)
plot(maha, "Mahalanobis Distance", color.purple)
```
## Inputs, outputs, expected behavior
**Kalman filter** (`f_kalman_init`, `update_scalar`, `predict`):
- **Inputs**: `nfeat` (int, 1-16 typical), `P0/Q0/R0` (float, initial noise estimates), `measurement` (float), `H` (float, observation matrix row)
- **Outputs**: Updated state vector `x` (array), NIS value `lastnis` (float, unbounded), ready flag `ready` (bool)
- **Edge cases**: Returns unmodified state if measurement is NA, requires ≥20 bars for adaptive Q/R to stabilize
**Changepoint detection** (`f_changepoint_nis`, `f_changepoint_cusum`, `f_changepoint_bocpd`):
- **Inputs**: `nis` (float, typically from Kalman filter), `threshold` (float, 9.0 for 99% confidence), `hazard` (float, 0.01-0.1 for BOCPD)
- **Outputs**: Changepoint probability (float, ) or binary flag (bool)
- **Edge cases**: CUSUM resets on boundary crossing, BOCPD requires ≥10 bars for stable run-length distribution
**Entropy functions** (`f_entropy_binary`, `f_entropy_ternary`, `f_entropy_combo`):
- **Inputs**: `data` (array, direction codes: -1/0/1), `window` (int, 20-100 typical)
- **Outputs**: Shannon entropy (float, ), max entropy = 1.0 for binary, 1.585 for ternary
- **Edge cases**: Returns 0.0 if all elements identical, handles empty arrays gracefully
**Microstructure metrics** (`f_get_micro_state`, `f_get_scientific_delta`, `f_get_aggregated_volume`):
- **Inputs**: `timeframe` (string, "1" for 1-minute), `lookback` (int, bars to aggregate)
- **Outputs**: Delta (float, signed volume), aggression (float, ), imbalance (float, )
- **Edge cases**: Returns NA if lower timeframe data unavailable, requires Premium/Pro account for intraday request.security
**Trajectory store** (`f_trajectory_new`, `push`, `read`):
- **Inputs**: `snap_dim` (int, state vector length), `capacity` (int, max snapshots), `offset` (int, 0=latest)
- **Outputs**: Snapshot array (array, length `snap_dim`)
- **Edge cases**: Returns NA-filled array if offset exceeds filled count, circular overwrite after capacity reached
## Limitations
1. **Kalman filter assumes linear dynamics**: The transition matrix F is diagonal (no cross-feature coupling). For non-linear systems (e.g., price-volatility feedback loops), the filter may diverge. Extended Kalman Filter (EKF) or Unscented Kalman Filter (UKF) variants are not implemented.
2. **Changepoint detection requires tuning**: NIS threshold (default 9.0) assumes Gaussian measurement noise. In heavy-tailed distributions (crypto, low-liquidity assets), false positives increase. CUSUM and BOCPD require manual hazard/boundary tuning per asset and timeframe.
3. **Microstructure functions require lower timeframe data**: `f_get_micro_state` and related functions call request.security with `timeframe="1"` (1-minute). This fails on daily/weekly charts or for symbols without intraday data. Users must handle NA returns or pre-filter symbols.
4. **Memory overhead for high-dimensional Kalman**: An N=16 feature Kalman filter requires 136 floats for packed covariance (16×17/2) plus state vector. On PulseWire's 50,000 float limit per script, this restricts other arrays. Reduce `nfeat` or use sparse feature selection.
5. **Entropy calculations assume discrete states**: Binary/ternary entropy requires pre-discretized input (direction codes -1/0/1). Continuous price data must be manually binned. The library does not auto-discretize or suggest bin counts.
6. **No multi-step prediction**: The Kalman filter supports one-step-ahead prediction only. For multi-bar forecasts (e.g., "predict price 5 bars ahead"), users must manually iterate the predict step, which compounds uncertainty without re-measurement.
7. **Adaptive Q/R convergence time**: Adaptive noise parameters require 20-50 bars to stabilize after initialization or regime change. During this period, filter estimates may be suboptimal. Consider using fixed Q/R for the first 50 bars, then enabling adaptation.
Library

CyberLearningLib# CyberLearningLib v4
CyberLearningLib provides online learning primitives for Pine Script traders building adaptive machine learning indicators, including circular training buffers, feature scaling, stochastic gradient descent (SGD), and distance metrics for k-nearest neighbors (kNN) algorithms.
## What it does
LearningLib delivers four core components: circular training buffers for memory-efficient sample storage (O(1) push/read), feature scalers with exponentially weighted moving average (EWMA) normalization, SGD optimizers with gradient clipping and multiple loss functions (squared, hinge, logistic, Huber), and distance metrics for kNN classification (Euclidean, Manhattan, Cosine, Mahalanobis, Chebyshev). Traders use these tools to build indicators that learn from historical price patterns—for example, a kNN classifier predicting next-bar direction based on the 10 most similar historical setups, with features auto-scaled via EWMA to handle non-stationary markets.
The library outputs trained model weights (SGD state vector), scaled feature vectors (normalized to or z-scores), distance matrices for kNN queries, and sample metadata (timestamp, sample type, trade direction). All data structures use circular buffers to maintain constant memory usage regardless of training duration, critical for long-running indicators on PulseWire's 50,000 float limit.
## How it works
The training buffer uses a circular array with write-head indexing: when capacity is reached, new samples overwrite the oldest. Each sample stores a feature vector (array), label (float, regression target or {-1,+1} for classification), weight (float, for importance sampling), timestamp (bar_index), sample type (enum: LIVE/SIMULATED/SHADOW/BACKFILL), trade direction (LONG/SHORT/FLAT), and two free metadata integers for custom categorization. The `read(offset)` method retrieves samples in reverse chronological order (0 = most recent), while `read_chrono(pos)` accesses samples in insertion order (0 = oldest).
Feature scaling supports three methods:
1. **EWMA normalization**: Maintains running mean/variance via `μ_t = (1-α)μ_{t-1} + αx_t`, scales features to z-scores
2. **Min-max scaling**: Tracks rolling min/max over window, normalizes to
3. **Percentile-based**: Uses IQR (interquartile range) for outlier-resistant scaling
SGD updates follow the standard formula `w_t = w_{t-1} - η∇L(w)` where η is learning rate and ∇L is loss gradient. Supported loss functions:
- **Squared**: `0.5(y - ŷ)²`, gradient = `-(y - ŷ)`
- **Hinge**: `max(0, 1 - y·ŷ)` for y ∈ {-1,+1}, gradient = `-y` if margin violated
- **Logistic**: `log(1 + exp(-y·ŷ))`, gradient = `-y / (1 + exp(y·ŷ))`
- **Huber**: Squared loss for small errors (|err| ≤ δ), linear for large errors (robust to outliers)
Gradient clipping prevents exploding gradients: `g_clipped = g / max(1, ||g|| / threshold)`. The library also provides gated SGD updates that skip parameter changes when innovation (measurement error) is below a threshold, reducing overfitting to noise.
Distance metrics compute similarity between feature vectors for kNN:
- **Euclidean**: `√Σ(x_i - y_i)²`
- **Manhattan**: `Σ|x_i - y_i|`
- **Cosine**: `1 - (x·y) / (||x|| ||y||)` (angle-based, scale-invariant)
- **Mahalanobis**: `√((x-y)'Σ⁻¹(x-y))` where Σ is covariance (accounts for feature correlations)
- **Chebyshev**: `max_i |x_i - y_i|` (L∞ norm)
## Why this is original
CyberLearningLib is the only PulseWire library providing a complete online learning toolkit with memory-efficient circular buffers and production-ready SGD implementations. Existing ML libraries either use linear arrays (memory grows unbounded), lack feature scaling (assume stationary data), or implement only Euclidean distance (ignoring feature correlations).
Unique features:
- **Circular training buffers**: O(1) push/read with constant memory, critical for indicators running 24/7 on crypto markets. No other Pine library offers circular indexing with chronological/reverse-chronological access.
- **Sample type tracking**: LIVE/SIMULATED/SHADOW/BACKFILL enum enables mixed training sets (e.g., "train on LIVE samples only, use SIMULATED for validation"). Essential for walk-forward optimization and out-of-sample testing.
- **Gated SGD updates**: Skip weight updates when innovation < threshold, preventing overfitting during low-volatility regimes. Based on Kalman filter innovation gating, not found in standard ML libraries.
- **Huber loss with configurable δ**: Robust regression loss that transitions from squared (δ-sensitive) to linear (outlier-resistant). Most Pine implementations use fixed δ=1.0; this library exposes δ as parameter.
- **Mahalanobis distance with shrinkage**: Accounts for feature correlations via inverse covariance, with Ledoit-Wolf shrinkage to prevent singular matrix errors. No other Pine library implements this (most use Euclidean only).
The library is designed for composition: training buffers feed into feature scalers, scaled features feed into SGD or kNN, distances feed into weighted voting. This modular design enables complex workflows (e.g., "scale features via EWMA, train linear SVM via hinge loss, classify new samples via kNN with Mahalanobis distance") without code duplication.
## How to use it
```pine
//@version=6
indicator("CyberLearningLib Demo", overlay=false)
import cybermediaboy/CyberLearningLib/4 as LL
import cybermediaboy/NumLib/5 as N
// Example 1: Circular training buffer
var tb = LL.f_buffer_new(capacity=100, nfeat=3)
if not na(close)
var features = array.from(ta.rsi(close, 14), ta.atr(14), volume)
float label = close < close ? 1.0 : -1.0 // Next-bar direction
var sample = LL.f_sample_new(features, label, LL.SampleType.LIVE,
LL.TradeDirection.LONG, meta_a=0, meta_b=0)
tb.push(sample)
// Read most recent sample
var recent = tb.read(0)
if not na(recent)
plot(recent.label, "Last Label", color.blue)
// Example 2: Feature scaling (EWMA)
var scaler = LL.f_scaler_new(nfeat=3, alpha=0.1)
if not na(close)
var raw_features = array.from(close, volume, ta.rsi(close, 14))
var scaled = scaler.scale(raw_features)
plot(array.get(scaled, 0), "Scaled Close", color.orange)
// Example 3: SGD training (hinge loss for binary classification)
var sgd = LL.f_sgd_new(nfeat=3, learning_rate=0.01, loss=LL.LossKind.HINGE)
if tb.filled >= 10
var train_sample = tb.read(0)
if not na(train_sample)
sgd.update(train_sample.features, train_sample.label, clip_threshold=5.0)
float prediction = sgd.predict(train_sample.features)
plot(prediction, "SGD Prediction", color.green)
// Example 4: kNN distance calculation
if tb.filled >= 2
var s1 = tb.read(0)
var s2 = tb.read(1)
if not na(s1) and not na(s2)
float dist_euclidean = LL.f_distance(s1.features, s2.features, LL.DistanceKind.EUCLIDEAN)
float dist_cosine = LL.f_distance(s1.features, s2.features, LL.DistanceKind.COSINE)
plot(dist_euclidean, "Euclidean Dist", color.red)
plot(dist_cosine, "Cosine Dist", color.purple)
```
## Inputs, outputs, expected behavior
**Training buffer** (`f_buffer_new`, `push`, `read`, `read_chrono`):
- **Inputs**: `capacity` (int, 50-1000 typical), `nfeat` (int, feature dimension), `offset/pos` (int, sample index)
- **Outputs**: TBSample (struct with features, label, metadata) or na if index out of bounds
- **Edge cases**: Returns na for invalid offsets, overwrites oldest sample at capacity, `filled` count saturates at capacity
**Feature scaler** (`f_scaler_new`, `scale`, `update`):
- **Inputs**: `nfeat` (int), `alpha` (float, EWMA decay 0.01-0.3 typical), `features` (array)
- **Outputs**: Scaled feature vector (array, z-scores or normalized)
- **Edge cases**: Returns unscaled features on first call (no history), handles NA elements via nz()
**SGD optimizer** (`f_sgd_new`, `update`, `predict`):
- **Inputs**: `nfeat` (int), `learning_rate` (float, 0.001-0.1 typical), `loss` (enum), `features/label` (float), `clip_threshold` (float, 1.0-10.0)
- **Outputs**: Prediction (float, unbounded for regression, {-1,+1} for classification after sign()), updated weights (internal state)
- **Edge cases**: Gradient clipping prevents exploding weights, returns 0.0 prediction before first update
**Distance metrics** (`f_distance`, `f_distance_mahalanobis`):
- **Inputs**: `x/y` (array, same length), `kind` (enum), `cov_inv` (array, tri-packed inverse covariance for Mahalanobis)
- **Outputs**: Distance (float, ≥0 for Euclidean/Manhattan/Chebyshev, for Cosine, unbounded for Mahalanobis)
- **Edge cases**: Returns NA if array lengths mismatch, Mahalanobis requires non-singular covariance (use shrinkage if needed)
**Sample filtering** (`by_type`, `by_direction`):
- **Inputs**: `tb` (TrainingBuffer), `st` (SampleType enum), `dir` (TradeDirection enum)
- **Outputs**: Filtered array (subset of buffer matching criteria)
- **Edge cases**: Returns empty array if no matches, preserves chronological order
## Limitations
1. **Fixed feature dimension**: Training buffers and scalers require `nfeat` declared at initialization. Changing feature count mid-stream requires creating a new buffer/scaler. Dynamic feature sets (e.g., "use 3 features on stocks, 5 on crypto") are not supported.
2. **No automatic hyperparameter tuning**: Learning rate, loss function, gradient clip threshold, and EWMA alpha must be manually specified. The library does not provide grid search, cross-validation, or adaptive learning rate schedules (e.g., Adam, RMSprop). Users must tune via backtesting.
3. **SGD assumes i.i.d. samples**: Stochastic gradient descent converges optimally when samples are independent and identically distributed. Financial time series violate this (autocorrelation, regime changes). For non-stationary data, consider using gated updates or periodically resetting weights.
4. **Mahalanobis distance requires covariance matrix**: Computing inverse covariance for N features requires O(N³) operations and N(N+1)/2 storage. For high-dimensional features (N > 10), this becomes computationally expensive. Use Euclidean or Cosine distance for N > 10, or apply PCA to reduce dimensionality first.
5. **No mini-batch SGD**: The library implements single-sample (online) SGD only. Mini-batch updates (averaging gradients over K samples) are not supported. For noisy gradients, increase EWMA alpha in feature scaling or use Huber loss instead of squared loss.
6. **Circular buffer overwrites without warning**: When capacity is reached, `push()` silently overwrites the oldest sample. If you need to preserve all historical data, implement external archiving (e.g., export to CSV via log.info) before buffer fills.
7. **Distance metrics do not handle missing features**: If a feature vector contains NA, distance functions return NA. The library does not impute missing values (mean, median, forward-fill). Users must handle NA via nz() or filtering before calling distance functions.
Library

CyberNumLib# CyberNumLib v5
CyberNumLib provides stateless numerical primitives for Pine Script traders who need advanced statistical calculations, robust normalization methods, and mathematical functions not available in PulseWire's native library.
## What it does
NumLib delivers 56 pure functions covering five categories: mathematical polyfills (hyperbolic functions, normal distribution CDF/inverse, error function), advanced smoothing filters (Ehlers Super Smoother, Butterworth, Savitzky-Golay), robust statistics (median-MAD, IQR-based scaling, Winsorized bounds), normalization methods (z-scores, percentile ranks, min-max scaling), and correlation analysis (Pearson, Spearman, Kendall, Hurst exponent). Traders use these functions to build custom indicators requiring statistical rigor beyond Pine's built-in ta.* namespace—for example, calculating confidence intervals from normal quantiles, applying outlier-resistant smoothing to noisy price data, or measuring non-linear correlation between assets.
The library outputs standardized numerical values ready for downstream indicator logic: z-scores for mean-reversion signals, normalized coefficients for ML feature engineering, correlation matrices for multi-asset analysis, and smoothed series for trend detection. All functions are stateless (no internal state variables), making them composable and predictable across different timeframes and symbols.
## How it works
NumLib implements well-documented statistical algorithms with explicit citations. The normal CDF uses the Abramowitz & Stegun 26.2.17 polynomial approximation (max error ~7e-8), while the inverse normal CDF employs the Beasley-Springer-Moro rational approximation for converting probabilities to z-scores with ~1e-10 precision—critical for quantile-based risk calculations. Hyperbolic tangent (tanh) is computed via the numerically stable identity `(e^(2x) - 1) / (e^(2x) + 1)` with argument clamping to ±20 to prevent math.exp overflow.
Smoothing filters follow Ehlers' DSP methodology: the Super Smoother is a 2-pole IIR Butterworth-equivalent with coefficients derived from `exp(-1.414π/len)`, providing lag reduction vs simple moving averages while suppressing high-frequency noise. The Savitzky-Golay filter uses fixed polynomial coefficients (order 2, length 13) for edge-preserving smoothing without phase shift.
Robust statistics leverage percentile-based methods resistant to outliers. The median-MAD estimator computes scale as `(Q75 - Q25) / 0.7413`, where 0.7413 is the IQR-to-standard-deviation conversion factor for normal distributions. Correlation functions implement textbook formulas: Pearson via covariance normalization, Spearman via rank transformation, Kendall via concordant-discordant pair counting. The Hurst exponent uses rescaled range (R/S) analysis to detect mean-reversion (H < 0.5) vs trending (H > 0.5) regimes.
## Why this is original
NumLib fills critical gaps in Pine Script's native math library. PulseWire provides no hyperbolic functions (tanh, sinh, cosh), no normal distribution quantile functions, no Savitzky-Golay smoothing, and no robust statistics beyond basic percentiles. Existing public libraries either bundle these functions with unrelated indicator logic (mixing calculation with rendering) or implement simplified versions without numerical stability guards.
This library is the only PulseWire publication offering:
- **Numerically stable implementations**: tanh with overflow clamping, normal CDF with Abramowitz-Stegun precision, inverse CDF with Beasley-Springer-Moro accuracy
- **Robust statistics suite**: median-MAD, IQR normalization, Winsorization—essential for outlier-resistant indicators in volatile markets
- **Ehlers DSP filters**: Super Smoother and Butterworth implementations with exact coefficient formulas from Ehlers' published work
- **Comprehensive correlation toolkit**: Pearson, Spearman, Kendall, plus Hurst exponent for regime detection—all in one dependency-free library
No other Pine library combines these four categories with explicit algorithm citations and edge-case handling (NA guards, zero-division checks, warmup period validation).
## How to use it
```pine
//@version=6
indicator("CyberNumLib Demo", overlay=false)
import cybermediaboy/CyberNumLib/5 as N
// Example 1: Z-score with robust median-MAD scaling
= N.f_basis_median_mad(close, 50)
plot(z_robust, "Robust Z-Score", color.blue)
// Example 2: Smooth price with Ehlers Super Smoother
smooth_close = N.f_supersmoother(close, 20)
plot(smooth_close, "Super Smooth", color.orange)
// Example 3: Calculate correlation between two assets
// (Assumes you have arrays x_data and y_data populated)
var x_arr = array.new(50)
var y_arr = array.new(50)
array.push(x_arr, close)
array.push(y_arr, volume)
if array.size(x_arr) > 50
array.shift(x_arr)
array.shift(y_arr)
corr_pearson = N.f_pearson(x_arr, y_arr, 50)
plot(corr_pearson, "Pearson Correlation", color.green)
// Example 4: Convert confidence level to z-score
conf_95 = 0.95
z_95 = N.f_norm_inv((1.0 + conf_95) / 2.0) // Returns ~1.96
plot(z_95, "95% Confidence Z", color.red)
```
## Inputs, outputs, expected behavior
**Smoothing functions** (`f_supersmoother`, `f_buttersmooth`, `f_savgol_2_13`):
- **Inputs**: `src` (float, typically close/high/low), `len` (int, window size 5-100 typical)
- **Outputs**: Smoothed float value, range matches input series
- **Edge cases**: Returns input value on first bar (no warmup), handles NA via nz()
**Statistical functions** (`f_zscore`, `f_basis_median_mad`, `f_percentile_bands`):
- **Inputs**: `src` (float series), `len` (int, minimum 10 for stability)
- **Outputs**: Z-scores (unbounded float), percentiles (price units), scale factors (positive float)
- **Edge cases**: Returns 0.0 for z-score if stdev = 0, returns NA for insufficient data (bar_index < len)
**Correlation functions** (`f_pearson`, `f_spearman`, `f_kendall`, `f_hurst_rs`):
- **Inputs**: `array` (length ≥ 10), `len` (int, sample size)
- **Outputs**: Correlation coefficient for Pearson/Spearman/Kendall, Hurst
- **Edge cases**: Returns 0.0 if array size < len, handles NA elements via filtering
**Math polyfills** (`f_tanh`, `f_norm_cdf`, `f_norm_inv`, `f_erf`):
- **Inputs**: Float values (unbounded for tanh/erf, for norm_inv, any for norm_cdf)
- **Outputs**: Bounded floats (tanh: , sigmoid: , norm_cdf: , norm_inv: unbounded)
- **Edge cases**: Clamps extreme inputs to prevent overflow (tanh at ±20, norm_inv at )
**Normalization functions** (`f_normalize`, `f_iqr_normalize`, `f_tanh_norm`):
- **Inputs**: `value` (float), `minval/maxval` (float bounds) or `len` (int window)
- **Outputs**: Normalized float in for f_normalize, for tanh-based methods
- **Edge cases**: Returns 0.0 if range is zero, handles NA inputs gracefully
## Limitations
1. **No dynamic array sizing**: Correlation functions require pre-allocated arrays of fixed size. If your data stream length varies, you must manage array resizing externally (e.g., via array.push + array.shift pattern). The library does not auto-resize or buffer data.
2. **Warmup period required**: Statistical functions (z-score, percentile bands, correlation) return unreliable values during the first `len` bars. Indicators using NumLib should display a warmup warning (e.g., "Insufficient data: need 50 bars") or gate signals until `bar_index >= len`.
3. **Precision limits on extreme inputs**: Math polyfills use polynomial approximations with documented error bounds (e.g., normal CDF ~7e-8, erf ~1.5e-7). For applications requiring higher precision (e.g., options pricing), these approximations may be insufficient. Extreme inputs (|x| > 20 for tanh, p < 1e-10 for norm_inv) are clamped to prevent overflow, which can distort tail probabilities.
4. **Correlation functions assume stationarity**: Pearson, Spearman, and Kendall correlations are computed over rolling windows without detrending. In strongly trending markets, these measures may overstate correlation due to common trend components. For non-stationary data, consider differencing the series first or using Hurst exponent to detect regime changes.
5. **No built-in significance testing**: The library returns raw correlation coefficients without p-values or confidence intervals. Traders must implement their own significance tests (e.g., t-test for Pearson correlation) or use rule-of-thumb thresholds (|r| > 0.7 for strong correlation).
6. **Single-threaded execution**: All functions execute sequentially on each bar. For indicators calling multiple NumLib functions per bar (e.g., computing 10 correlations), execution time may exceed PulseWire's script timeout on lower timeframes with large datasets. Optimize by caching results or reducing calculation frequency.
Library

CandlePressure_UtilitiesCandlePressure_Utilities is a lightweight Pine library for converting raw OHLC candle structure into a normalized candle-pressure score, buy/sell percentage estimates, oscillator output, and compact display helpers.
The library is designed for scripts that want a reusable candle-pressure layer without rebuilding the same CLV/body/wick math every time.
It centralizes the pieces that commonly repeat across pressure-based scripts:
• close-location value / CLV calculation
• candle body dominance
• upper-vs-lower wick imbalance
• deadzone-filtered wick pressure
• normalized pressure output from -1 to +1
• buy/sell percentage conversion
• pressure oscillator conversion from -100 to +100
• alternate body/wick buy-sell allocation
• compact volume and relative-volume formatting
• table/label size and table-position helpers
• small percent and black/white text helpers
On the example chart, the pressure candles, pressure oscillator, buy/sell split, CLV/body/wick breakdown, alternate body/wick comparison, and compact table values are all materially driven by this library.
This library is intentionally focused on pure candle structure. It does not confirm trend, detect pivots, calculate RSI/DMI/ATR context, decide trade direction, or choose final signal logic for the calling script. Those layers remain script-level decisions.
➖Quick Start➖
Import the library near the top of your script in global scope, alongside any other imports, before calling its helpers.
Typical placement:
//@version=6
indicator(...) or strategy(...)
import MYNAMEISBRANDON/CandlePressure_Utilities/1 as cp
Replace /1 with the latest published version if a newer version is available.
The main helper for most scripts is candlePressureMetrics(), which returns:
• pressure
• buyPct
• sellPct
Example:
= cp.candlePressureMetrics(
open,
high,
low,
close,
volume)
string splitText = cp.fmtBuySellSplit(
buyPct,
sellPct,
volume)
float pressureOsc = cp.pressureOsc(
pressure)
The library uses standard OHLCV argument order:
open, high, low, close, volume
➖What The Library Measures➖
The default candle-pressure model uses:
• Wick Deadzone = 0.02
• CLV Weight = 0.55
• Body Weight = 0.30
• Wick Weight = 0.15
CLV measures where the close finished inside the candle range. Body contribution measures open-to-close directional dominance. Wick contribution measures lower-wick vs upper-wick imbalance.
The final pressure score is a weighted blend of those components, normalized from -1 to +1.
That pressure score can then be converted into buy/sell percentage estimates, a -100 to +100 pressure oscillator, candle-overlay colors, table values, labels, or dashboard outputs.
➖Function Reference➖
These helpers are grouped by purpose.
Most scripts will only need:
• candlePressureMetrics()
• fmtBuySellSplit()
• pressureOsc()
More advanced scripts can use the full component helpers for tables, tooltips, debug output, or custom pressure models.
➖Model + Math Helpers➖
modelDefaults()
Returns the default candle-pressure model values used by this library.
Returns:
Wick deadzone, CLV weight, body weight, wick weight
clamp(v, lo, hi)
Restricts a value between a lower and upper bound.
Parameters:
v (float): Input value
lo (float): Lower bound
hi (float): Upper bound
Returns:
Clamped value
safeDiv(numerator, denominator, fallback)
Safely divides two values and returns the fallback when division is not valid.
Parameters:
numerator (float): Numerator value
denominator (float): Denominator value
fallback (float): Value returned when division is unsafe
Returns:
numerator / denominator, or fallback when unsafe
➖Display + UI Helpers➖
fmtCompact(val, sigFigs, naText)
Formats large values into compact display text such as 1.5k, 2.4m, or 1.2b.
Parameters:
val (float): Value to format
sigFigs (simple int): Significant figures to keep
naText (simple string): Text returned when val is na
Returns:
Compact formatted string
fmtBuySellSplit(buyPct, sellPct, volumeValue)
Formats buy/sell percentages into rounded split text such as 62/38.
Parameters:
buyPct (float): Buy percentage
sellPct (float): Sell percentage
volumeValue (float): Volume value used to handle missing or no-volume bars
Returns:
Formatted buy/sell split text
contrastText(bg)
Chooses black or white text based on background brightness.
Parameters:
bg (color): Background color
Returns:
Readable contrast text color
stripLeadingZero(txt)
Removes the leading zero from decimal text.
Parameters:
txt (string): Input text
Returns:
Adjusted text, such as 0.25 -> .25 or -0.25 -> -.25
fmtRelVol(val, naText)
Formats relative volume with two decimals and strips the leading zero.
Parameters:
val (float): Relative volume value
naText (string): Text returned when val is na
Returns:
Formatted relative-volume text
pctChange(currentValue, baseValue)
Returns the percent change from a base value.
Parameters:
currentValue (float): Current or projected value
baseValue (float): Comparison baseline
Returns:
Percent change
fmtPctWhole(val, naText)
Formats a percent value as rounded whole-percent text.
Parameters:
val (float): Percent value
naText (string): Text returned when val is na
Returns:
Rounded percent string
pctInt(pct)
Rounds and clamps a percentage into 0–100 integer form.
Parameters:
pct (float): Percent value
Returns:
Integer percent from 0 to 100
pctIntVol(pct, volumeValue)
Rounds and clamps a percentage into 0–100 integer form, returning 0 on no-volume bars.
Parameters:
pct (float): Percent value
volumeValue (float): Volume value
Returns:
Integer percent from 0 to 100
tableTextSize(sizeText)
Converts user-facing table-size text into Pine table text-size enums.
Parameters:
sizeText (string): Size text. Expected values: "Tiny", "Small", "Normal", or "Large"
Returns:
Pine table text-size enum
labelSize(sizeText)
Converts user-facing label-size text into Pine label-size enums.
Parameters:
sizeText (string): Size text. Expected values: "Tiny", "Small", "Normal", "Large", or "Huge"
Returns:
Pine label-size enum
tablePos(posText)
Converts user-facing table-position text into Pine table position enums.
Parameters:
posText (string): Table position text
Returns:
Pine table position enum
bw(useBlack)
Returns black text when the condition is true, otherwise white.
Parameters:
useBlack (bool): Whether black text should be used
Returns:
Black or white text color
➖Candle Pressure Helpers➖
candlePressurePartsFull(openValue, highValue, lowValue, closeValue, wickDeadzone, weightClv, weightBody, weightWick)
Converts OHLC candle structure into the full normalized pressure component set.
Parameters:
openValue (float): Candle open
highValue (float): Candle high
lowValue (float): Candle low
closeValue (float): Candle close
wickDeadzone (float): Wick imbalance threshold below which wick contribution is forced to 0
weightClv (float): Weight assigned to the CLV component
weightBody (float): Weight assigned to the body component
weightWick (float): Weight assigned to the wick component
Returns:
CLV, body % of range, signed body term, raw wick imbalance, deadzoned wick imbalance, final pressure
Note:
wickDeadzone, weightClv, weightBody, and weightWick are optional. If omitted, the library uses its default model:
Wick Deadzone 0.02 / CLV 0.55 / Body 0.30 / Wick 0.15
candlePressureParts(openValue, highValue, lowValue, closeValue, wickDeadzone, weightClv, weightBody, weightWick)
Converts OHLC candle structure into the compact pressure component set.
Parameters:
openValue (float): Candle open
highValue (float): Candle high
lowValue (float): Candle low
closeValue (float): Candle close
wickDeadzone (float): Wick imbalance threshold below which wick contribution is forced to 0
weightClv (float): Weight assigned to the CLV component
weightBody (float): Weight assigned to the body component
weightWick (float): Weight assigned to the wick component
Returns:
CLV, body % of range, raw wick imbalance, deadzoned wick imbalance, final pressure
Note:
wickDeadzone, weightClv, weightBody, and weightWick are optional. If omitted, the library uses its default model:
Wick Deadzone 0.02 / CLV 0.55 / Body 0.30 / Wick 0.15
pressureToBuySell(pressure, volumeValue)
Converts normalized pressure into buy/sell percentages.
Parameters:
pressure (float): Candle pressure in the -1..+1 range
volumeValue (float): Volume value used to handle missing or no-volume bars
Returns:
Buy %, Sell %
pressureOsc(pressure)
Converts normalized pressure into a -100..+100 oscillator value.
Parameters:
pressure (float): Candle pressure in the -1..+1 range
Returns:
Pressure oscillator value
candlePressureMetrics(openValue, highValue, lowValue, closeValue, volumeValue, wickDeadzone, weightClv, weightBody, weightWick)
One-call convenience wrapper for scripts that need final pressure, buy %, and sell %.
Parameters:
openValue (float): Candle open
highValue (float): Candle high
lowValue (float): Candle low
closeValue (float): Candle close
volumeValue (float): Volume value used to handle missing or no-volume bars
wickDeadzone (float): Wick imbalance threshold below which wick contribution is forced to 0
weightClv (float): Weight assigned to the CLV component
weightBody (float): Weight assigned to the body component
weightWick (float): Weight assigned to the wick component
Returns:
Pressure, Buy %, Sell %
Note:
wickDeadzone, weightClv, weightBody, and weightWick are optional. If omitted, the library uses its default model:
Wick Deadzone 0.02 / CLV 0.55 / Body 0.30 / Wick 0.15
bodyWickRateBuyPct(openValue, highValue, lowValue, closeValue)
Returns an alternate buy percentage using body/wick structure only.
Parameters:
openValue (float): Candle open
highValue (float): Candle high
lowValue (float): Candle low
closeValue (float): Candle close
Returns:
Buy percentage
bodyWickRateBuySell(openValue, highValue, lowValue, closeValue, volumeValue)
Returns alternate body/wick buy and sell percentages.
Parameters:
openValue (float): Candle open
highValue (float): Candle high
lowValue (float): Candle low
closeValue (float): Candle close
volumeValue (float): Volume value used to handle missing or no-volume bars
Returns:
Buy %, Sell %
➖Important Notes➖
Candle Pressure is not order flow.
The buy/sell split produced by this library is an estimate derived from candle structure. It is not true bid/ask volume, footprint data, or exchange-level order flow.
The pressure model is intentionally pure OHLC structure:
• CLV measures where the close finished inside the candle range.
• Body contribution measures open-to-close directional dominance.
• Wick contribution measures lower-wick vs upper-wick imbalance.
• Final pressure is a weighted blend of those components.
Momentum filters such as RSI, DMI, ATR, trend state, relative volume, or multi-timeframe context should be added by the calling script when needed.
This library provides the reusable candle-pressure foundation only.
➖Release Notes➖
v1
Initial release of CandlePressure_Utilities.
This release provides a focused candle-pressure utility layer for Pine scripts that need reusable OHLC pressure calculations, buy/sell percentage estimates, pressure oscillator output, compact display formatting, and small table/label helper functions.
Included in this release:
• default candle-pressure model values
• safe math helpers
• compact number formatting
• buy/sell split formatting
• relative-volume formatting
• table/label size and table-position helpers
• percent and bias display helpers
• full candle-pressure component output
• compact candle-pressure component output
• pressure-to-buy/sell conversion
• pressure oscillator conversion
• alternate body/wick buy-sell allocation
The library is designed to stay focused on reusable candle-pressure mechanics. It does not decide trend, trade direction, signal confirmation, pivot structure, RSI/DMI filters, ATR filters, or final color logic. Calling scripts remain responsible for their own signal model and visual interpretation.
Library
