Lookup

IntIndex & Lookup Policy

The adaptive integer-key directory behind every typed store. It maps each key to a slot, chooses dense addressing, a map, binary search or a scan from the keys it sees, and leaves the payload to you.

Updated Publisher Alien_AlgorithmsPine Script v6

A key-to-slot directory

A typed store holds one value per key. When a key owns several fields, such as a price, a side and a label, an IntIndex resolves the key once and every payload array uses the same slot. Slots follow insertion order from 0, so slot n belongs to the nth key added.

Build and query

A build has three steps: buildBegin(), then add() or addMany(), then buildFinish(), which seals the index and chooses the lookup.

One directory, two payload arrayspine
var op.IntIndex trades = op.intIndex()
var array<float> entryPrices = array.new<float>()
var array<string> sides = array.new<string>()

if barstate.isfirst
    trades.buildBegin()
    trades.addMany(array.from(7005, 7001, 7042))
    trades.buildFinish()

    // Slots follow insertion order: 7005 is slot 0, 7001 slot 1, 7042 slot 2.
    entryPrices.push(101.5)
    entryPrices.push(99.0)
    entryPrices.push(104.25)
    sides.push("long")
    sides.push("short")
    sides.push("long")

int slot = trades.find(7001)
float entryPrice = slot >= 0 ? entryPrices.get(slot) : na
string side = slot >= 0 ? sides.get(slot) : na

// Output: trade 7001 resolves to slot 1, entry price 99.0, side "short".
  • find(key) returns the slot, or -1 on a miss. findMany(queryKeys, output) resolves a batch in query order with one dispatch.
  • keyAt(slot) reads a slot's key, and keysView() is every key in slot order, as a read-only view.
  • add() is O(1). buildFinish() is O(entries) for most builds, and O(entries log entries) when unordered keys need sorting.
  • Collections are reused across rebuilds, so rebuilding an index every bar allocates nothing new.
Order matters in strict mode
A lookup before buildFinish(), or an add() after it, stops the script. Call buildBegin() to rebuild. With strictLifecycle = false, the lookup returns -1 and the add starts a new build instead.

Automatic selection rules

In automatic mode, the default, buildFinish() checks these rules in order and takes the first that matches:

KeysLookupfind() cost
None, or no more than linearMaxEntries (0 by default)Linear scanO(n)
Consecutive and ascendingArithmetic dense: the slot is the key minus the smallest keyO(1)
Span within 16,384 and within twice the entry countDense slot tableO(1)
Not ascending, up to 50,000 keysMapO(1)
Ascending, up to 50,000 keys, and expectedQueries at least max(32, 10% of the entries)MapO(1)
Anything elseSorted binary searchO(log n)
  • The span is the largest key minus the smallest, plus one.
  • A strictly ascending build searches the key array directly, with no extra storage. Any other order sorts a copy once.
  • With autoPromote on, a sorted index of up to 50,000 keys promotes itself to a map once its counted queries reach the same threshold as the hint. op.indexConfigDynamic() turns this on.

Forcing a lookup

A LookupPolicy in the configuration overrides automatic mode. Each preset sets one:

PresetPolicySuits
op.indexConfigStaticSorted()sortedA directory built once and queried across many bars. No map is ever allocated.
op.indexConfigDense()denseValidated dense integer keys. Raises when the span exceeds maxDenseSpan.
op.indexConfigDictionary()dictionaryA persistent map for lookup-heavy work, up to 50,000 keys.
op.indexConfigTiny()linearA very small directory rebuilt far more often than it is queried. No build cost at all.
op.indexConfigDynamic()automaticOnline promotion and counters, for query volume that is unknown in advance.

mode() reports the lookup in use. It is never automatic.

Duplicate keys

A DuplicatePolicy decides what a repeated key means:

PolicyEffect
op.DuplicatePolicy.raiseErrorThe default. buildFinish() stops the script and reports how many duplicates it found. Every lookup representation verifies uniqueness.
op.DuplicatePolicy.firstWinsEvery lookup resolves to the slot of the first occurrence.
op.DuplicatePolicy.lastWinsEvery lookup resolves to the slot of the last occurrence.

IndexConfig fields

op.indexConfig() takes every field as a named argument and clamps each one to a valid range. One configuration can be shared by several indexes; change it between builds, not during a query loop.

FieldDefaultMeaning
policyautomaticThe requested lookup.
duplicatesraiseErrorWhat a repeated key means.
promoteQueryRatio0.10Queries per entry that justify building a map.
promoteMinQueries32Minimum queries before a map is justified.
autoPromotefalseLet a sorted index promote itself to a map while queried.
validateAscendingtrueCheck key order during the build. When false, buildBegin(expectedAscending) is trusted.
maxDenseSpan100000Largest span a forced dense lookup accepts.
autoDenseSpanRatio2.0Automatic dense tables need span ≤ entries × this. 0 disables them.
autoDenseMaxSpan16384Absolute span ceiling for automatic dense tables.
linearMaxEntries0Builds this small use a linear scan in automatic mode.
duplicateScanLimit64Largest unordered linear build checked pair by pair for duplicates before sorting instead.
collectStatsfalseCount queries, hits and misses.
strictLifecycletrueStop the script on calls out of order.

Inspecting an index

stats() returns an IndexStats snapshot: the requested and active lookups, entries, queries, hits and misses, builds, promotions, duplicates, the key span, and whether the keys were ascending or consecutive. Queries are counted when collectStats is on or a promotion is armed; hits and misses only with collectStats.

Clearing and resetting

  • clear() empties the index, keeps every allocation and the configuration, and keeps the cumulative build and promotion counters.
  • reset(settings) also replaces the configuration and zeroes those counters.

Calls

CallWhat it does
op.intIndex(settings = na)Creates an index with automatic defaults or the given configuration.
index.buildBegin(expectedAscending = true)Starts a build and clears the previous keys.
index.add(key), index.addMany(keyBatch)Add keys. Each returns the slot of the first key it added.
index.buildFinish(expectedQueries = na)Seals the index and returns the lookup it chose.
index.find(key), index.contains(key)Resolve one key to its slot, or test it.
index.findMany(queryKeys, output = na)Resolve a batch in query order. -1 marks a miss.
index.keyAt(slot), index.keysView()Read keys by slot.
index.entryCount(), index.mode(), index.sealed(), index.stats()Size, active lookup, build state and telemetry.
index.clear(), index.reset(settings = na)Empty the index, or empty it and replace the configuration.
op.indexConfig(...)A configuration. Also the five presets above.

Every signature is in the API Reference, and configuration in IndexConfig. Next: IntBuckets.

OptiPine is a performance library for Pine Script. TradingView's memory, execution and collection limits still apply. © Alien_Algorithms.