Transcribing a TradingView Script
Naxbot’s strategy scripting conventions, syntax, and native function naming have been heavily inspired by TradingView’s PineScript . While PineScript is its own language, Naxbot’s script engine makes use of Lua under the hood, so PineScript strategies cannot be simply copy-pasted to Naxbot and expected to function properly.
However, transcribing PineScript strategies / indicators to Naxbot is fairly straightforward, and can be done even without a deep understanding of either of the two ecosystems.
In most languages, a variable that represents a list of values is referred to as an array. In Lua these are referred to as tables, but Naxbot provides its own array type, which you’ll want to use for most operations. They look and behave very similar in most situations, but Naxbot arrays are much more performant to work with.
So, Lua tables = bad, Naxbot arrays = good!
Basic Principles
- Both Naxbot and PineScript treat arrays / tables as first-class citizen and support using basic arithmetic operators
(
+,-,*,/,^) on them. - PineScript allows you to declare functions that naturally and recursively operate on individual values of an array, whereas in Naxbot, you would have to make use of a for-loop.
- PineScript supports recursive assignments on variables, whereas Naxbot doesn’t.
- PineScript supports using conditionals like
>,<,<=,>=directly on arrays, whereas in Naxbot, you need to make use of helper functions likegt,lt,leq,geq, andlif.
Many basic indicators that can be found within PineScript under the ta. global variable are available in Naxbot
under the same name, and more are being added with every update. Consult our
API Reference for a full list.
Examples
PineScript conditionals
Take the following PineScript function, where val is an input array:
round_(val) => val > .99 ? .999 : val < -.99 ? -.999 : valThis function definition compares val element-wise. In Naxbot, an equivalent function would look as follows:
function round_(val)
return lif(gt(val, 0.99), 0.999, lif(lt(val, -0.99), -0.999, val))
endAlternatively, you can also use a for-loop. Note however that this approach is not as performant as using Naxbot’s helper functions:
function round_(val)
for i=1,#val do
val[i] = val[i] > 0.99 and 0.999 or (val[i] < -0.99 and -0.999 or val[i])
end
endRecursive Assignments
In PineScript, the following is a valid assignment to value:
value = 0.0
value := .66 * ((hl2 - low) / (high - low) - .5) + .67 * nz(value[1])In Naxbot, this would have to be implemented using a for-loop, due to the presence of value[1]:
local value = num_array()
for i=1,#hl2 do
value:push(0.66 * ((hl2[i] - low[i]) / (high[i] - low[i]) - 0.5) + 0.67 * (i > 1 and value[i-1] or 0))
end- In the for-loop, all array-variables, i.e.
hl2,low,high, andvaluehave to be indexed, to retrieve a specific value for the particular element that is being calculated. - Instead of
nz, we’re manually checking for the validity ofvalue[i-1], Naxbot’snzonly works on arrays. - We’re using
#hl2(the length ofhl2) as the upper bound for the for-loop, but#low,#highor even#closeor#volumewould also work (they are all guaranteed to be of the same length).
Sample Transcriptions
Below you will find some example transcriptions of TradingView indicators, to give you a general idea of what to look out for when transcribing your own.
If you are in search for ready-to-go Naxbot indicators, or would like to publish your own, head on over to the Naxbot discord!
Fisher Transform
PineScript
//@version=5
indicator(title="Fisher Transform", shorttitle="Fisher", format=format.price, precision=2, timeframe="", timeframe_gaps=true)
len = input.int(9, minval=1, title="Length")
high_ = ta.highest(hl2, len)
low_ = ta.lowest(hl2, len)
round_(val) => val > .99 ? .999 : val < -.99 ? -.999 : val
value = 0.0
value := round_(.66 * ((hl2 - low_) / (high_ - low_) - .5) + .67 * nz(value[1]))
fish1 = 0.0
fish1 := .5 * math.log((1 + value) / (1 - value)) + .5 * nz(fish1[1])
fish2 = fish1[1]
hline(1.5, "1.5", color=#E91E63)
hline(0.75,"0.75", color=#787B86)
hline(0, "0", color=#E91E63)
hline(-0.75, "-0.75", color=#787B86)
hline(-1.5, "-1.5", color=#E91E63)
plot(fish1, color=#2962FF, title="Fisher")
plot(fish2, color=#FF6D00, title="Trigger")Naxbot
function fisher_transform(length)
local fish_high = highest(hl2, length)
local fish_low = lowest(hl2, length)
local value = num_array()
for i=1,#hl2 do
value:push(0.66 * ((hl2[i] - fish_low[i]) / (fish_high[i] - fish_low[i]) - 0.5) + 0.67 * ((value[i-1] == nil or value[i-1] ~= value[i-1]) and 0 or value[i-1]))
end
value = nz(value)
value = lif(lt(value, -0.99), -0.999, value)
value = lif(gt(value, 0.99), 0.999, value)
local fish = num_array()
local logval = ln((1 + value) / (1 - value))
for i=1,#hl2 do
fish:push(0.5 * logval[i] + 0.5 * ((fish[i-1] == nil or fish[i-1] ~= fish[i-1]) and 0 or fish[i-1]))
end
fish = nz(fish)
local fish_2 = fish(1)
return {
fisher = fish,
trigger = fish_2,
}
endIchimoku Cloud
PineScript
//@version=5
indicator(title="Ichimoku Cloud", shorttitle="Ichimoku", overlay=true)
conversionPeriods = input.int(9, minval=1, title="Conversion Line Length")
basePeriods = input.int(26, minval=1, title="Base Line Length")
laggingSpan2Periods = input.int(52, minval=1, title="Leading Span B Length")
displacement = input.int(26, minval=1, title="Lagging Span")
donchian(len) => math.avg(ta.lowest(len), ta.highest(len))
conversionLine = donchian(conversionPeriods)
baseLine = donchian(basePeriods)
leadLine1 = math.avg(conversionLine, baseLine)
leadLine2 = donchian(laggingSpan2Periods)
plot(conversionLine, color=#2962FF, title="Conversion Line")
plot(baseLine, color=#B71C1C, title="Base Line")
plot(close, offset = -displacement + 1, color=#43A047, title="Lagging Span")
p1 = plot(leadLine1, offset = displacement - 1, color=#A5D6A7,
title="Leading Span A")
p2 = plot(leadLine2, offset = displacement - 1, color=#EF9A9A,
title="Leading Span B")
plot(leadLine1 > leadLine2 ? leadLine1 : leadLine2, offset = displacement - 1, title = "Kumo Cloud Upper Line", display = display.none)
plot(leadLine1 < leadLine2 ? leadLine1 : leadLine2, offset = displacement - 1, title = "Kumo Cloud Lower Line", display = display.none)
fill(p1, p2, color = leadLine1 > leadLine2 ? color.rgb(67, 160, 71, 90) : color.rgb(244, 67, 54, 90))Naxbot
function donchian(length)
return avg({ lowest(low, length), highest(high, length) })
end
function ichimoku_cloud(conversion_periods, base_periods, lagging_span_2_periods, displacement)
local conversion_line = donchian(conversion_periods)
local base_line = donchian(base_periods)
local lead_line = avg({conversion_line, base_line})
local lead_line2 = donchian(lagging_span_2_periods)
local cloud_upper_line = lif(gt(lead_line, lead_line2), lead_line, lead_line2)
local cloud_lower_line = lif(lt(lead_line, lead_line2), lead_line, lead_line2)
return {
conversion_line = conversion_line,
base_line = base_line,
lagging_span = close(-displacement + 1),
cloud_upper_line = cloud_upper_line(displacement - 1),
cloud_lower_line = cloud_lower_line(displacement - 1),
}
endConnors RSI
PineScript
//@version=5
indicator(title="Connors RSI", shorttitle="CRSI", format=format.price, precision=2, timeframe="", timeframe_gaps=true)
src = close
lenrsi = input(3, "RSI Length")
lenupdown = input(2, "UpDown Length")
lenroc = input(100, "ROC Length")
updown(s) =>
isEqual = s == s[1]
isGrowing = s > s[1]
ud = 0.0
ud := isEqual ? 0 : isGrowing ? (nz(ud[1]) <= 0 ? 1 : nz(ud[1])+1) : (nz(ud[1]) >= 0 ? -1 : nz(ud[1])-1)
ud
rsi = ta.rsi(src, lenrsi)
updownrsi = ta.rsi(updown(src), lenupdown)
percentrank = ta.percentrank(ta.roc(src, 1), lenroc)
crsi = math.avg(rsi, updownrsi, percentrank)
plot(crsi, "CRSI", #2962FF)
band1 = hline(70, "Upper Band", color = #787B86)
hline(50, "Middle Band", color=color.new(#787B86, 50))
band0 = hline(30, "Lower Band", color = #787B86)
fill(band1, band0, color.rgb(33, 150, 243, 90), title = "Background")Naxbot
function updown(series)
series = series or close;
local is_equal = eq(series, series(1))
local is_growing = gt(series, series(1))
local ud = num_array()
for i=1,#close do
local prev = (ud[i-1] == nil or ud[i-1] ~= ud[i-1]) and 0 or ud[i-1]
ud:push(is_equal[i] and 0 or (is_growing[i] and (prev <= 0 and 1 or prev + 1) or (prev >= 0 and -1 or prev - 1)))
end
return ud
end
function connors_rsi(length_rsi, length_updown, length_roc)
length_rsi = length_rsi or 3
length_updown = length_updown or 2
length_roc = length_roc or 100
local rsi_1 = rsi(close, length_rsi)
local updown_rsi = rsi(updown(close), length_updown)
local p_rank = percentrank(roc(close, 1), length_roc)
local c_rsi = avg({rsi_1, updown_rsi, p_rank})
return c_rsi
end