📰 Latest: HaasOnline Academy Is Back — Structured Education for Smarter Trade Bots
Account
Converting Other Languages

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_index has no direct equivalent. If you need to track bar counts, use a counter saved with Save().
  • PineScript's barstate.isrealtime and barstate.isconfirmed do not exist. Use TimeIntervals and the fullCandles parameter 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 toolsline.new(), label.new(), box.new(), etc. do not exist. HaasScript provides Plot*() commands for charting but no freeform drawing.
  • barstate functions — no isrealtime, 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.