Skip to Content
Advanced TopicsTranscribing a TradingView Script

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.

Note

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 like gt, lt, leq, geq, and lif.
💡
Tip

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 : val

This 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)) end

Alternatively, 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 end

Recursive 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
Note
  • In the for-loop, all array-variables, i.e. hl2, low, high, and value have 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 of value[i-1], Naxbot’s nz only works on arrays.
  • We’re using #hl2 (the length of hl2) as the upper bound for the for-loop, but #low, #high or even #close or #volume would 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.

💡
Tip

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, } end

Ichimoku 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), } end

Connors 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