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

Intervals vs timers

Intervals vs Timers

In HaasScript, "timers" can refer to two completely different concepts with different use cases. Understanding the distinction is critical for writing effective trading scripts:

  • Performance timers (StartTimer, GetTimer, StopTimer) - For measuring execution time during development
  • Interval-based timing (Time(), AdjustTimestamp, CreateTimestamp, OptimizedForInterval) - For actual trading logic, scheduling actions, and working with time-based data

This page focuses on interval-based timing for trading logic.

Current Time: Time()

The Time() command returns the current Unix timestamp in UTC. This is your foundation for all time-based operations in HaasScript.

local currentTime = Time()
Log('Current Unix timestamp: ' .. currentTime)

Unix timestamps represent the number of seconds since January 1, 1970, 00:00:00 UTC. They're timezone-independent, which simplifies time calculations in trading scripts.

Creating Timestamps: CreateTimestamp

When you need a specific timestamp (not the current time), use CreateTimestamp:

-- Create a timestamp for a specific date/time
local tradeTime = CreateTimestamp(2026, 4, 13, 10, 30, 0)  -- April 13, 2026, 10:30:00 UTC

-- Partial timestamps use current values for omitted parameters
local todayAtNoon = CreateTimestamp(2026, 4, 13, 12, 0)  -- Seconds default to current
local thisYear = CreateTimestamp(2026)  -- Uses current month, day, time

This is useful for:

  • Scheduling trades at specific times
  • Defining trading session boundaries
  • Creating time-based filters

Adjusting Timestamps: AdjustTimestamp

AdjustTimestamp modifies a timestamp by adding or subtracting time units:

local currentTime = Time()

-- Add 1 hour
local oneHourLater = AdjustTimestamp(currentTime, 0, 0, 1)

-- Add 1 day
local tomorrow = AdjustTimestamp(currentTime, 0, 0, 0, 1)

-- Add 30 minutes and subtract 5 seconds
local adjustedTime = AdjustTimestamp(currentTime, -5, 30)

-- Add 1 week (7 days)
local nextWeek = AdjustTimestamp(currentTime, 0, 0, 0, 7)

Parameters are (in order): unix timestamp, addSeconds, addMinutes, addHours, addDays, addMonths, addYears. All time addition parameters are optional and default to zero.

Common Time-Based Patterns

Trading Session Filter

Only trade during specific hours:

local currentTime = Time()
-- Extract hour from timestamp (using current date context)
local currentHour = CurrentHour(currentTime)

-- Only trade between 9 AM and 5 PM UTC
if currentHour >= 9 and currentHour <= 17 then
    -- Your trading logic here
    local rsi = RSI(ClosePrices(), 14)
    if rsi < 30 then
        Log('Executed buy trade')
    end
end

Cooldown Period

Prevent rapid successive trades:

-- Store last trade time in script data
local lastTradeTime = Load('lastTradeTime')
local currentTime = Time()
local cooldownMinutes = 30

-- Check if enough time has passed since last trade
if lastTradeTime == nil or currentTime >= AdjustTimestamp(lastTradeTime, 0, cooldownMinutes) then
    -- Execute trade
    local rsi = RSI(ClosePrices(), 14)
    if rsi < 30 then
        Log('Executed buy trade')
        Save('lastTradeTime', currentTime)
    end
end

Time-Based Exits

Close positions after a certain duration:

local position = PositionContainer()

if position.isLong or position.isShort then
    local openTime = position.OpenTime
    local currentTime = Time()
    local maxHoldHours = 24

    -- Check if position has been open too long
    if currentTime >= AdjustTimestamp(openTime, 0, 0, maxHoldHours) then
        -- Close position
        if position.isLong then
            DoExitPosition('Max Hold Long')
        elseif position.isShort then
            DoExitPosition('Max Hold Short')
        end
    end
end

Scheduled Action

Execute an action at a specific time:

local currentTime = Time()
local targetTime = CreateTimestamp(2026, 4, 13, 10, 0, 0)  -- 10 AM UTC

-- Check if we've reached or passed target time
if currentTime >= targetTime then
    -- One-time action
    Log('Scheduled action executed at 10 AM UTC')

    -- Prevent re-execution by storing a flag
    local executed = Load('scheduledActionDone')
    if executed == nil then
        Save('scheduledActionDone', true)
        -- Your action here
    end
end

Interval Optimization: OptimizedForInterval

When working with interval-based data (like closed candles), OptimizedForInterval significantly speeds up backtesting by caching calculations:

-- Calculate RSI only when 1-hour candle closes, cache result
local rsiHourly = OptimizedForInterval(60, function()
    return RSI(ClosePrices(), 14)
end)

-- Use the cached value
if rsiHourly < 30 then
    DoLong()
end

The interval parameter is in minutes. Common intervals:

  • 15 = 15 minutes
  • 60 = 1 hour
  • 1440 = 1 day

Set interval to 0 to use the main chart interval.

DefineIntervalOptimization for Custom Commands

When creating custom commands that should update only on interval ticks, use DefineIntervalOptimization:

-- Define a custom command that updates every 1 hour
DefineIntervalOptimization(60)

function CalculateHourlyRSI()
    local rsi = RSI(ClosePrices(), 14)
    return rsi
end

-- The command will only recalculate when the 1-hour interval ticks
local hourlyRSI = CalculateHourlyRSI()

Only use this when your command specifically needs updates once per interval tick. This optimization is designed for backtesting performance.

Practical Example: Time-Based Strategy

Here's a complete example combining multiple timing concepts:

-- Only trade during London session (8 AM - 4 PM UTC)
local currentTime = Time()
local currentHour = tonumber(CurrentDate(currentTime):sub(12, 13))
local inSession = currentHour >= 8 and currentHour <= 16

-- Get hourly RSI (cached, updates on 1-hour candle close)
local rsiHourly = OptimizedForInterval(60, function()
    return RSI(ClosePrices(), 14)
end)

-- Check cooldown since last trade
local lastTradeTime = Load('lastTradeTime')
local cooldownMinutes = 60
local cooldownPassed = lastTradeTime == nil or currentTime >= AdjustTimestamp(lastTradeTime, 0, cooldownMinutes)

-- Execute trade if all conditions met
if inSession and cooldownPassed then
    if rsiHourly < 30 then
        DoLong()
        Save('lastTradeTime', currentTime)
    elseif rsiHourly > 70 then
        DoShort()
        Save('lastTradeTime', currentTime)
    end
end

Time-Based vs Update-Based Logic

Understanding the difference is crucial:

Update-based (runs every update cycle):

local rsi = RSI(ClosePrices(), 14)  -- Recalculated every update
if rsi < 30 then
    DoLong()  -- Can trigger multiple times per candle
end

Time/interval-based (runs only when conditions met):

local rsiHourly = OptimizedForInterval(60, function()
    return RSI(ClosePrices(), 14)  -- Only when 1-hour candle closes
end)

-- Trades 1-hour signals
if rsiHourly < 30 then
    DoLong()
end

Common Mistakes

Using timers for time-based logic:

-- "Wrong": Performance timers don't persist
StartTimer()

if GetTimer() > 5 then
    -- Only executes if it took more than 5ms to get here after timer started
    DoSomething()
end

Forgetting UTC timezone:

-- Remember: Time() returns UTC, not local time
local currentTime = Time()  -- Always UTC

Not accounting for interval in OptimizedForInterval:

-- Make sure interval matches your intended timeframe
local rsi15min = OptimizedForInterval(60, function()  -- runs every hour
    return RSI(ClosePrices(15), 14) -- uses 15-minute prices
end)