📰 Latest: HaasOnline Academy Is Back — Structured Education for Smarter Trade Bots
Account
Using Data

Collection Access Methods

Collection Access Methods

HaasScript price data and indicator results are returned as HaasNumberCollection objects. These collections behave like arrays but have special properties that make them convenient for trading strategies.

HaasNumberCollection Basics

A HaasNumberCollection represents time series data—typically price data or indicator values. Commands like ClosePrices(), RSI(), and MACD() all return collections:

local closePrices = ClosePrices()  -- Collection of close prices
local rsi = RSI(closePrices, 14)   -- Collection of RSI values
local macd = MACD(closePrices, 12, 26, 9)  -- Collection of MACD values

Automatic Value Extraction

When you use a HaasNumberCollection (without an index) in comparisons, HaasScript automatically uses the first (latest) value at index 1:

local rsi = RSI(ClosePrices(), 14)

-- Direct comparison - uses latest RSI value automatically
if rsi < 30 then
    DoLong()
end

-- Arithmetic applies to all values in the collection
local atr = ATR(HighPrices(), LowPrices(), ClosePrices(), 14)
local atrPercentage = (atr / ClosePrices()) * 100  -- Applies to entire collections

Important: Do NOT use .Value to access the current value. Use the collection directly in comparisons:

-- WRONG: .Value is not needed
if rsi.Value < 30 then
    DoLong()
end

-- CORRECT: Use collection directly
if rsi < 30 then
    DoLong()
end

Indexing Collections

Collections are 1-indexed, meaning index 1 is the latest value and higher indexes go back in time.

Important: When you access a collection with an index like collection[3], it returns a subarray starting from that index, not just a single value:

local closePrices = ClosePrices()

-- Returns subarray from index 3 onwards
local priceFrom3 = closePrices[3]  -- This is an array: [closePrices[3], closePrices[4], ...]

-- To get a single value, use it in a comparison or arithmetic context
if closePrices[3] > 10000 then
    Log('Price at index 3 is greater than 10000')
end

When you assign collection[index] to a variable, you get the subarray from that index onwards:

local prices = ClosePrices()
local recentPrices = prices[3]  -- Gets array from index 3 to end

Critical: Do NOT use index 0. This will throw an error:

-- WRONG: Index 0 doesn't exist
local price = closePrices[0]  -- Error!

-- CORRECT: Use index 1 for latest value
local price = closePrices[1]

Collection Order

Price collections are sorted from new to old (most recent to oldest):

local closePrices = ClosePrices()

-- Index 1 = most recent candle
-- Index 2 = candle before that
-- Index N = oldest candle in the collection

This is intuitive for trading logic where you typically care about the most recent data first.

Subarray Access

Accessing collection[index] returns the subarray from that index onwards:

local prices = {100, 105, 110, 108, 112}

local subarray = prices[3]  -- Returns: {110, 108, 112}
-- This is the array starting from index 3 to the end

Use this when you need to work with a portion of the collection from a specific point onwards.

Collection Size

Use the Count() command to get the number of elements in a collection:

local closePrices = ClosePrices()
local count = Count(closePrices)

Log('Number of price points: ' .. count)

Context-Based Behavior

HaasNumberCollections have special smart behavior. Regular arrays (tables) do NOT have this behavior and will throw errors.

HaasNumberCollection in comparisons: Uses single value at the specified index:

local rsi = RSI(ClosePrices(), 14)  -- Returns HaasNumberCollection

if rsi[3] > 2 then
    Log('Value at index 3 is greater than 2')  -- Uses single value at index 3
end

HaasNumberCollection in arithmetic: Applies to all values:

local closePrices = ClosePrices()  -- Returns HaasNumberCollection
local multiplier = 10

local result = closePrices * multiplier  -- All values multiplied by 10

HaasNumberCollection when assigned: Returns subarray from that index onwards:

local closePrices = ClosePrices()

local subarray = closePrices[3]  -- Returns: {prices[3], prices[4], ...}

HaasNumberCollection without index: Uses first value (index 1) in comparisons:

local rsi = RSI(ClosePrices(), 14)

if rsi < 30 then  -- Uses rsi[1] automatically
    DoLong()
end

Regular arrays (tables) behave like standard Lua arrays:

local array = {1, 2, 3, 4, 5}

-- array * 3  -- ERROR: attempt to perform arithmetic on a table value
-- array > 3  -- ERROR: attempt to compare table with number
-- array[1]  -- Returns: 1 (single value, not subarray)

Iterating Collections

Loop through collection elements using standard for loops:

local closePrices = ClosePrices()

-- Iterate through all price points
for i = 1, Count(closePrices) do
    local price = closePrices[i]
    Log('Price at index ' .. i .. ': ' .. price)
end

Indicator Collections

Indicators return collections calculated from the input price collection:

local closePrices = ClosePrices()

-- RSI collection
local rsi = RSI(closePrices, 14)

-- Each RSI value corresponds to a price point
-- In comparisons, collection[index] uses single value at that index
Log('Current RSI: ' .. rsi[1])      -- RSI for most recent candle
Log('Previous RSI: ' .. rsi[2])     -- RSI for previous candle

Collection Arithmetic

HaasNumberCollections support arithmetic operations:

local closePrices = ClosePrices()

-- Multiply all values by 10
local scaledPrices = closePrices * 10

-- Add 5 to all values
local shiftedPrices = closePrices + 5

When you use indexed collections in arithmetic, it operates on the subarrays:

local closePrices = ClosePrices()

-- Calculate percentage change from 5 candles ago
-- closePrices[1] and closePrices[5] return subarrays
-- In arithmetic, subarrays use their first values
local percentChange = ((closePrices[1] - closePrices[5]) / closePrices[5]) * 100
Log('5-candle change: ' .. percentChange .. '%')

Important: Regular arrays (tables) do not support arithmetic operations and will throw errors:

local array = {1, 2, 3, 4, 5}
-- array * 10  -- ERROR: attempt to perform arithmetic on a table value

Practical Examples

RSI Crossover Detection

local rsi = RSI(ClosePrices(), 14)

-- Compare values at specific indices directly
-- HaasNumberCollections use single value at index in comparisons

-- Detect RSI crossing below 70 (overbought to neutral)
if rsi[2] >= 70 and rsi[1] < 70 then
    Log('RSI crossed below 70 - potential exit signal')
end

-- Detect RSI crossing above 30 (oversold to neutral)
if rsi[2] <= 30 and rsi[1] > 30 then
    Log('RSI crossed above 30 - potential entry signal')
end

Price Change Calculation

local closePrices = ClosePrices()

-- Calculate 3-period price change
-- Log() displays values, HaasNumberCollections show first value
Log('Current price: ' .. closePrices[1])
Log('Price 3 candles ago: ' .. closePrices[3])

-- For arithmetic on specific values, use indexed collections directly
local priceChange = closePrices[1] - closePrices[3]
local percentChange = (priceChange / closePrices[3]) * 100

Log('Price change over 3 periods: ' .. percentChange .. '%')

Moving Average Comparison

local closePrices = ClosePrices()
local sma20 = SMA(closePrices, 20)
local sma50 = SMA(closePrices, 50)

-- Current price relative to moving averages
-- In comparisons, indexed collections use single values
local aboveSMA20 = closePrices[1] > sma20[1]
local aboveSMA50 = closePrices[1] > sma50[1]

-- Moving average crossover
local goldenCross = sma20[1] > sma50[1] and sma20[2] <= sma50[2]
local deathCross = sma20[1] < sma50[1] and sma20[2] >= sma50[2]

if goldenCross then
    Log('Golden cross detected - potential bullish signal')
elseif deathCross then
    Log('Death cross detected - potential bearish signal')
end

Multiple Timeframe Analysis

-- Get prices from different timeframes
local hourlyClose = ClosePrices(3600)    -- 1 hour
local dailyClose = ClosePrices(86400)    -- 1 day

-- Calculate RSI on both timeframes
local hourlyRsi = RSI(hourlyClose, 14)
local dailyRsi = RSI(dailyClose, 14)

-- Trade only when both timeframes align
-- Collections use first value in comparisons
if hourlyRsi < 30 and dailyRsi < 30 then
    Log('Oversold on both 1h and 1d timeframes')
    DoLong()
end

ATR-Based Stop Loss

local closePrices = ClosePrices()
local atr = ATR(HighPrices(), LowPrices(), closePrices, 14)

-- ATR is returned as a collection
-- Convert ATR to percentage for StopLoss - indexed collections use single values in arithmetic
local atrPercentage = (atr[1] / closePrices[1]) * 100

StopLoss(atrPercentage * 2)

MACD Signal Line Crossover

local macd = MACD(ClosePrices(), 12, 26, 9)

-- MACD returns collection with multiple series
-- In comparisons, indexed collections use single values

-- MACD bullish crossover (MACD crosses above signal)
if macd[2] <= macd[4] and macd[1] > macd[3] then
    Log('MACD bullish crossover')
    DoLong()
end

-- MACD bearish crossover (MACD crosses below signal)
if macd[2] >= macd[4] and macd[1] < macd[3] then
    Log('MACD bearish crossover')
    DoShort()
end

Collections vs Arrays

HaasNumberCollections and regular arrays (tables) have fundamentally different behavior:

Feature HaasNumberCollection Array (Table)
Created by Indicator commands {} syntax
Indexing 1-based 1-based
array[index] returns Subarray from index Single value from index
Without index in comparison Uses first value Error
Arithmetic operations Applies to all values Error
Typical use Price data, indicators Custom data storage

HaasNumberCollections have special smart behavior:

  • collection[3] assigned to variable → subarray from index 3 onwards
  • collection[3] in comparison → single value at index 3
  • collection * number → arithmetic on all values
  • collection < 30 → uses first value in comparison

Regular arrays (tables) behave like standard Lua arrays:

  • array[3] → single value at index 3
  • array * number → ERROR: attempt to perform arithmetic on a table value
  • array < 30 → ERROR: attempt to compare table with number
  • array[3] assigned to variable → single value, not subarray

Important: Only HaasNumberCollections have the smart behavior. Regular arrays created with {} syntax will throw errors if you try arithmetic or comparisons on them directly. Always index regular arrays before using them in operations.

Common Mistakes

Using .Value property:

-- WRONG: .Value is not recommended
if rsi.Value < 30 then
    DoLong()
end

-- CORRECT: Use collection directly
if rsi < 30 then
    DoLong()
end

Assuming array[index] returns single value:

-- RISKY: array[index] returns subarray when assigned
local priceFromIndex3 = closePrices[3]  -- This is an array, not a single value!
Save('pfi3', priceFromIndex3) -- Saves the whole subarray

-- CORRECT: Use ArrayGet with HNC's
local priceFromIndex3 = ArrayGet(closePrices, 3)  -- Single price value from index 3
Save('pfi3', priceFromIndex3) -- Saves only the price

Using index 0:

-- WRONG: Index 0 doesn't exist
local price = prices[0]

-- CORRECT: Use index 1 for latest value
local price = prices[1]

Assuming index order:

-- WRONG: Thinking index 1 is oldest
local oldestPrice = prices[1]

-- CORRECT: Index 1 is most recent
local latestPrice = prices[1]
local oldestPrice = prices[#prices]

Not accounting for collection length:

-- RISKY: May access non-existent index
local price = closePrices[100]

-- BETTER: Check length first
local count = Count(closePrices)
if count >= 100 then
    local price = closePrices[100]
end