Logging
Logging
Logging is your primary tool for debugging HaasScript. Whether you are tracing indicator values during a backtest, diagnosing why a bot is not taking trades, or monitoring live behavior, the logging commands give you visibility into your script's execution.
HaasScript does not use Lua's print() command. Instead, it provides its own set of logging functions that output directly to the bot's log panel.
Log()
The core logging command. It accepts any value and writes it to the log:
Log('Bot started')
Log(42, 'rgba(255, 128, 0, 0.5)')
Log(3.14159, Cyan, 'Prefix-', '-Suffix')
Log() accepts four parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
message |
dynamic | Yes | The value to log. Accepts numbers, strings, booleans, collections. |
color |
string | No | Color of the log message (hex, rgb, or rgba format). |
prefix |
string | No | Prepended to the message. |
suffix |
string | No | Appended to the message. |
Logging Variables
Use the .. concatenation operator to combine text with variable values:
local rsi = RSI(ClosePrices(), 14)
Log('RSI value: ' .. rsi)
For non-string values, use tostring() to ensure safe conversion:
local entryPrice = GetPositionEnterPrice()
Log('Entry price: ' .. tostring(entryPrice))
Prefix and Suffix
Organize log output by adding consistent labels:
local rsi = RSI(ClosePrices(), 14)
Log(rsi, '', '[RSI] ', '') -- For array values
Log('[RSI] ' .. rsi) -- For single value
local macd = MACD(ClosePrices(), 12, 26, 9)
Log(macd[1], '', '[MACD] ', '') -- For array values
Log('[MACD] ' .. macd[1]) -- For single value
Output:
MACD] 15.9714292928693
[MACD] [15.9714292928693, 15.9244209339959, ... ]
[RSI] 56.6763158876637
[RSI] [56.6763158876637, 63.038506075048, ... ]
Color-Coded Logging
Assign colors to visually distinguish message types in the log panel:
Log('Trade executed', 'green')
Log('Low balance detected', 'orange')
Log('Invalid parameter', 'red')
Colors accept hex (#00ff00), rgb (rgb(0, 255, 0)), or rgba (rgba(0, 255, 0, 0.8)) formats.
Logging Collections
HaasNumberCollections output their current (latest) value directly. To inspect historical values, access them by index:
local rsi = RSI(ClosePrices(), 14)
-- Current value
Log('Current RSI: ' .. rsi)
-- Previous values (1 = latest, 2 = one tick ago, etc.)
Log('Previous RSI: ' .. rsi[2])
Log('RSI two ticks ago: ' .. rsi[3])
LogError(), LogWarning(), LogWalletError()
HaasScript provides specialized logging commands that add visual distinction in the log panel:
| Command | Purpose | Panel Display |
|---|---|---|
LogError(message) |
Marks an error condition. | Red error icon. |
LogWarning(message) |
Marks a warning. | Yellow warning icon. |
LogWalletError(message) |
Wallet error and activates the trade amount error state. | Warning icon + stops trade execution. |
local direction = GetPositionDirection()
if direction == NoPosition then
LogWarning('No open position found')
end
LogWalletError is specifically for trade amount problems. It logs a warning and prevents the bot from placing further trades until the condition is resolved:
local tradeAmount = TradeAmount()
local minAmount = MinimumTradeAmount()
if tradeAmount < minAmount then
LogWalletError('Trade amount too small: ' .. tradeAmount)
end
Conditional Logging
Unnecessary log calls slow down backtests and clutter the log panel during live execution. Use conditions to log only when relevant:
Log on State Changes
Only log when a value changes:
local lastRSI = Load('lastRSI')
local rsi = RSI(ClosePrices(), 14)
if lastRSI == nil or rsi ~= lastRSI then
Log('RSI changed: ' .. lastRSI .. ' -> ' .. rsi)
Save('lastRSI', rsi)
end
Log on Trade Events
Log only when an action is taken:
local rsi = RSI(ClosePrices(), 14)
if rsi < 30 and GetPositionDirection() ~= PositionLong then
DoLong()
Log('Entered long - RSI: ' .. rsi)
end
Log Within a Debug Flag
Use an input field as a debug toggle so you can enable verbose logging without editing code:
local debugMode = Input('Debug Mode', false)
if debugMode then
Log('RSI: ' .. RSI(ClosePrices(), 14))
Log('MACD: ' .. MACD(ClosePrices(), 12, 26, 9)[1])
Log('Position: ' .. tostring(GetPositionDirection()))
end
Performance Logging: Timers
HaasScript provides timer commands for measuring how long a section of code takes to execute. This is useful for identifying slow calculations during backtesting:
StartTimer('calc')
-- Expensive calculation
local rsi = RSI(ClosePrices(), 14)
local macd = MACD(ClosePrices(), 12, 26, 9)
local bb = BBANDS(ClosePrices(), 20, 2.0, 2.0)
local elapsed = StopTimer('calc')
Log('Calculation time: ' .. elapsed .. 's')
Timer commands:
| Command | Description | Returns |
|---|---|---|
StartTimer([key]) |
Starts a named timer. | void |
GetTimer([key]) |
Gets elapsed time without stopping. | number (seconds) |
StopTimer([key]) |
Stops timer and returns elapsed time. | number (seconds) |
Use unique keys when timing multiple sections:
StartTimer('indicators')
local rsi = RSI(ClosePrices(), 14)
local macd = MACD(ClosePrices(), 12, 26, 9)
Log('Indicators: ' .. StopTimer('indicators') .. 's')
StartTimer('signals')
local signal = GetBuySellLevelSignal(rsi, 30, 70)
Log('Signals: ' .. StopTimer('signals') .. 's')
CustomReport() for Persistent Metrics
Log() output scrolls and is not retained between backtest runs. For metrics you want to see in backtest analysis alongside standard report fields, use CustomReport():
CustomReport('Signal Count', signalCount)
CustomReport('Avg RSI Entry', avgRSI)
CustomReport('Max Drawdown', maxDrawdown)
The first parameter is the label, the second is the value. Values appear in the Custom Report section of backtest results.
Overriding Built-in Report Fields
Pass "Override" as the third parameter to replace built-in report values with your own calculations:
CustomReport('RealizedProfit', myNetPnL, 'Override')
CustomReport('ROI', myROI, 'Override')
CustomReport('FeeCosts', myTotalFees, 'Override')
Supported override keys: GrossProfit, FeeCosts, RealizedProfit, UnrealizedProfit, ROI, BuyAndHodl, ClosedPositions, WinningPositions, AvgProfit, AvgPositionSize, AvgPositionMargin, FilledOrders, PartiallyFilledOrders, CancelledOrders, FailedOrders, BiggestWin, BiggestLoss, SharpeRatio, SortinoRatio, WinPercentage, ProfitFactor.
Clear an override by passing nil as the value:
CustomReport('ROI', 0, 'Override')
Finalize() for Backtest-Only Reports
Finalize() runs a callback only on the last update cycle of a backtest. This is ideal for summary metrics that would be wasteful to calculate on every tick:
local totalSignals = 0
local winningTrades = 0
-- Inside your trade logic...
totalSignals = totalSignals + 1
-- After trade closes profitably...
winningTrades = winningTrades + 1
-- Report only at the end of the backtest
Finalize(function()
CustomReport('Total Signals', totalSignals)
CustomReport('Win Rate', Round((winningTrades / totalSignals) * 100, 2) .. '%')
end)
DisableIndicatorContainerLogs
When using indicator containers, HaasScript automatically logs their output. In scripts with many containers, this produces significant noise. Disable it with:
DisableIndicatorContainerLogs()
Call this once near the top of your script, before any indicator container definitions.
Debugging Signal Flow
When a bot is not trading as expected, logging at each decision point reveals where the logic diverges from expectations:
local rsi = RSI(ClosePrices(), 14)
local macd = MACD(ClosePrices(), 12, 26, 9)
Log('RSI: ' .. rsi .. ' | MACD line: ' .. macd[1] .. ' | MACD signal: ' .. macd[3])
local rsiSignal = GetBuySellLevelSignal(rsi, 30, 70)
local macdValue = macd[1] - macd[3]
local macdSignal = GetThresholdSignal(macdValue, 0, 0.2)
Log('RSI signal: ' .. tostring(rsiSignal) .. ' | MACD signal: ' .. tostring(macdSignal))
local consensus = GetConsensusSignal(rsiSignal, macdSignal)
Log('Consensus: ' .. tostring(consensus))
if consensus ~= SignalNone then
Log('Executing trade on consensus: ' .. tostring(consensus))
DoSignal(consensus)
else
Log('No consensus - waiting')
end
Once the issue is identified, remove or guard the debug logs behind a flag before going live.
Common Mistakes
Using print() instead of Log():
-- Wrong: print() does not exist in HaasScript
print('RSI: ' .. rsi)
-- Correct
Log('RSI: ' .. rsi)
Logging on every tick in production:
-- Problem: floods the log during live execution
Log('RSI: ' .. RSI(ClosePrices(), 14))
-- Better: gate behind a debug flag
local debugMode = Input('Debug', false)
if debugMode then
Log('RSI: ' .. RSI(ClosePrices(), 14))
end
Not converting non-string values:
-- Can fail if the value is not a string or number
Log('Position: ' .. someValue)
-- Safer
Log('Position: ' .. tostring(someValue))
Relying on Log() for persistent metrics:
-- Log output is ephemeral - it scrolls away
Log('Win rate: 65%')
-- Use CustomReport for metrics you need to review later
CustomReport('Win Rate', '65%')