PineScript Part 1 - Introduction
Converting from PineScript to HaasScript
PineScript and HaasScript share similar goals — both are domain-specific languages for trading — but their execution models and syntax differ enough that a straight line-by-line translation does not work. This page covers the structural differences you need to understand. For detailed command mapping, see Command Reference. For a full strategy walkthrough, see Strategy Conversion.
No Script Declaration
PineScript requires a strategy() or study() call at the top of every script. HaasScript has no declaration — the script body runs directly:
-- PineScript
//@version=5
strategy("My Strategy", overlay=true)
-- HaasScript: just start writing
local rsi = RSI(ClosePrices(), 14)
HaasScript scripts are always strategies (the equivalent of study() does not exist — there are no indicator-only scripts). Charting is done through Plot() and related commands within any script.
Execution Model
PineScript processes the entire chart history bar-by-bar, then switches to realtime. On each historical bar, the script runs top-to-bottom once. HaasScript runs on a tick or interval basis — the platform invokes your script on each update cycle, and state between updates is preserved through Save() and Load().
In practice, this means:
- PineScript loops implicitly over all bars. HaasScript does not — your code runs once per tick.
- PineScript's
bar_indexhas no direct equivalent. If you need to track bar counts, use a counter saved withSave(). - PineScript's
barstate.isrealtimeandbarstate.isconfirmeddo not exist. Use TimeIntervals and thefullCandlesparameter on price commands instead.
Price Data Access
PineScript exposes price data as built-in variables. HaasScript uses function calls that return HaasNumberCollection:
| PineScript | HaasScript | Returns |
|---|---|---|
close |
ClosePrices() |
HaasNumberCollection |
open |
OpenPrices() |
HaasNumberCollection |
high |
HighPrices() |
HaasNumberCollection |
low |
LowPrices() |
HaasNumberCollection |
volume |
Volume() |
HaasNumberCollection |
HaasNumberCollection works both as an array and as a single value. When used in math or comparisons, it automatically resolves to the latest (index 1) value:
local rsi = RSI(ClosePrices(), 14)
-- Collection resolves to latest value automatically
if rsi < 30 then
DoLong()
end
Accessing historical values uses 1-based indexing, where 1 is the most recent:
local closePrices = ClosePrices()
local current = closePrices[1] -- Latest
local previous = closePrices[2] -- One tick ago
Indicator Functions
PineScript uses the ta. namespace for indicators. HaasScript uses top-level functions without a namespace:
-- PineScript
myRsi = ta.rsi(close, 14)
myMacd = ta.macd(close, 12, 26, 9)
-- HaasScript
local rsi = RSI(ClosePrices(), 14)
local macd = MACD(ClosePrices(), 12, 26, 9)
The indicator names are largely the same. The main adjustment is passing HaasNumberCollection instead of a single price variable. See Command Reference for the full mapping table.
Strategy Entry and Exit
PineScript uses named function calls with direction and size parameters. HaasScript uses separate commands for each action:
| PineScript | HaasScript |
|---|---|
strategy.entry("Long", strategy.long) |
DoLong() |
strategy.entry("Short", strategy.short) |
DoShort() |
strategy.close("Long") |
DoExitPosition() |
HaasScript's managed trading model handles position sizing automatically based on your bot configuration. You do not specify trade sizes in code (unless using unmanaged trading). Risk management is handled through dedicated commands:
-- Risk management (called once, not conditionally)
StopLoss(2.5)
TakeProfit(5.0)
TrailingStopLoss(1.5)
-- Entry/exit logic
local rsi = RSI(ClosePrices(), 14)
if rsi < 30 and GetPositionDirection() ~= PositionLong then
DoLong()
end
Checking current position state replaces PineScript's strategy.position_size:
-- PineScript
if strategy.position_size == 0
-- HaasScript
if GetPositionDirection() == NoPosition
If as Expression vs Statement
PineScript's if can return a value like a ternary operator:
// PineScript
color bgColor = close > open ? color.green : color.red
signalValue = rsi < 30 ? 1 : rsi > 70 ? -1 : 0
Lua's if is a statement and cannot return values. Use a regular if/elseif/else block:
local bgColor
if closePrices > openPrices then
bgColor = 'green'
else
bgColor = 'red'
end
For simple conditional values, use the Branch() command:
local bgColor = Branch(closePrices > openPrices, 'green', 'red')
Multi-Timeframe Data
PineScript uses request.security() to pull data from higher or lower timeframes. HaasScript handles this through the interval parameter on price data functions:
-- PineScript
higherRsi = request.security(syminfo.tickerid, "1D", ta.rsi(close, 14))
-- HaasScript
local dailyClose = ClosePrices(1440) -- 1440 minutes = 1 day
local dailyRsi = RSI(dailyClose, 14)
The interval is specified in minutes. Common intervals: 1, 5, 15, 60, 240, 1440.
Inputs
The concept is the same — expose configurable parameters — but the syntax differs:
-- PineScript
length = input.int(14, "RSI Length")
threshold = input.float(30.0, "Threshold")
-- HaasScript
local length = Input('RSI Length', 14)
local threshold = Input('Threshold', 30.0)
HaasScript's Input() auto-detects the type from the default value. It also supports a options parameter for dropdown menus:
local maType = Input('MA Type', 'SMA', {'SMA', 'EMA', 'WMA'})
Plotting
PineScript uses plot(), bgcolor(), and barcolor(). HaasScript provides a broader set of charting commands:
-- PineScript
plot(rsi, "RSI")
hline(30, "Oversold")
bgcolor(rsi < 30 ? color.green : na)
-- HaasScript
Plot(rsi, 'RSI', 'blue')
PlotHorizontalLine(30, 'Oversold', 'green')
MarkCandle(rsi < 30, 'green')
Key Syntax Differences
| PineScript | HaasScript / Lua |
|---|---|
// comment |
-- comment |
var myVar = 1 |
local myVar = 1 |
else if |
elseif |
and / or / not
|
and / or / not (same) |
== / != / > / <
|
== / ~= or !=/ > / < (same) |
String concat: str1 + str2
|
String concat: str1 .. str2
|
Arrays: array.new_int(0)
|
Arrays: HNC() or {}
|
#myArray (length) |
Count(myArray) or #myArray
|
na |
nil |
true / false
|
true / false (same) |
Ternary: cond ? a : b
|
No ternary — use if/else or Branch()
|
What PineScript Has That HaasScript Does Not
Some PineScript features have no direct equivalent:
-
Drawing tools —
line.new(),label.new(),box.new(), etc. do not exist. HaasScript providesPlot*()commands for charting but no freeform drawing. -
barstatefunctions — noisrealtime,isconfirmed,ishistory. Use interval and candle management instead. -
strategy.equity— no direct equity curve access within the script. Use backtest reports for equity data. - PineScript's built-in backtesting — HaasScript handles backtesting through the TradeServer platform, not within the script itself.