What's new

Level up your trading game with Naxbot

Naxbot R is the world's first fully programmable crypto trading bot with an integrated metaheuristic optimization engine. Join us today to automate the mundane aspects of trading, so you can spend more time crafting profitable strategies. Self-hosted with no DRM or licensing, buy once - own forever!

Script Engine Overview

  • Views Views: 959
  • Last updated Last updated:
  • Introduction​

    At the heart of Naxbot R's Scripting engine lies the Strategy.lua script. Naxbot expects this script to at least contain a function named process, as this function is used in order to generate indicator data.

    Naxbot expects this function to return a Table containing at least the following elements:
    • a boolean variable named long_entry_condition
    • a boolean variable named short_entry_condition
    • a number variable named tp_x where x is a nonzero positive integer for every crawler.tp_count you have set in your config.json
    • a number variable named stop_loss
    The most basic Strategy.lua would therefore look something like this:
    Lua:
    function process()
    return {
    long_entry_condition = constant(false),
    short_entry_condition = constant(false),
    tp_1 = constant(0),
    stop_loss = constant(0),
    }
    end

    Of course, the above script won't actually do anything. But before we can craft some actual functionality, we need to go over the basics of how Naxbot Scripting actually works.

    Documentation​

    Functions in this documentation will be annotated as follows:
    Code:
    function_name(required_argument: Type, [optional_argument]: Type) -> ReturnType
    Function description will be denoted with 💬, and common pitfalls with ⚠️.

    Variables​

    In Lua, variables can either be "global" or "local". A local variable is declared by preceding it with the word "local", whereas a global variable is declared without anything else, like so:
    Lua:
    -- local variable declaration
    local var = 0;

    -- global variable declaration
    globalvar = 0;

    Naxbot R provides some global variables out of the box for you to use in your strategies, namely the kline data in form of open, close, high, low, hl2, and ohlc4. These variables are provided in the form of arrays, which in Lua are simply tables that are indexed using numbers instead of strings.

    Also, in Lua, arrays start at index 1 (in most other programming languages, they start at 0). The reason for this is of a historic nature, and is not really relevant. But it is important to keep in mind for Scripting with Naxbot R.

    Arithmetic Operations​

    Most variables provided by Naxbot R are in the form of arrays. However, Naxbot R's scripting engine allows you to use basic arithmetic operators (+, -, *, /) on arrays that contain only numbers. These arithmetic operators get resolved into the following helper functions, which can also be called explicitly:

    Code:
    add(a: Array<number>, b: Array<number>) -> Array<number>
    💬 Adds a and b together.

    ⚠️ a and b must be of the same length.

    Code:
    sub(a: Array<number>, b: Array<number>) -> Array<number>
    💬 Subtracts b from a.

    ⚠️ a and b must be of the same length.

    Code:
    mul(a: Array<number>, b: Array<number>) -> Array<number>
    💬 Multiplies a and b.

    ⚠️ a and b must be of the same length.

    Code:
    div(a: Array<number>, b: Array<number>) -> Array<number>
    💬 Divides a over b.

    ⚠️ a and b must be of the same length.



    There is no difference in performance between using arithmetic operators or those helper functions. The following pieces of code perform the same:
    Lua:
    local arithmetic_hl2 = (high + low) / constant(2);
    local functional_hl2 = div(add(high, low), constant(2));

    ⚠️ NOTE: Arithmetic operators only work on arrays provided by Naxbot R, and on arrays obtained through arithmetic operations, indicators, or helper functions. Custom arrays do not support arithmetic operators by default:
    Lua:
    -- this is fine
    local const_2 = constant(2);
    local arithmetic_hl2 = (high + low) / const_2;

    -- this will error in the highlighted line
    local custom_const_2 = {};
    for i = 1,#close,1 do
    custom_const_2[i] = 2
    end
    local custom_hl2 = (high + low) / custom_const_2;

    This can be mitigated by either using a helper function instead of the arithmetic operator, or by passing your custom array through a different function (e.g. an indicator) first:
    Example 1:
    local custom_const_2 = {};
    for i = 1,#close,1 do
    custom_const_2[i] = 2
    end

    -- "nz" replaces all NaN values with 0, but
    -- leaves the array unchanged otherwise.
    -- the resulting array is compatible with
    -- arithmetic operators.
    custom_const_2 = nz(custom_const_2);
    local custom_hl2 = (high + low) / custom_const_2;
    Example 2:
    local custom_const_2 = {};
    for i = 1,#close,1 do
    custom_const_2[i] = 2
    end

    -- we can also use a helper function instead
    local custom_hl2 = div((high + low), custom_const_2);

    Like arithmetic operators in any other programming language, multiplication & division resolve before addition & subtraction, from left to right.

    All of the above helper functions can also be chained into each other. For example, the equation a + (b * c) / d would be represented as follows:
    Lua:
    add(a, div(mul(b, c), d));

    Since this has the potential to create long-winded and hard-to-read code however, you may want to consider using arithmetic operators wherever possible, or storing intermediate results in local variables like so:
    Lua:
    local b_times_c = mul(b, c);
    local b_times_c_over_d = div(b_times_c, d);
    local a_plus_b_times_c_over_d = add(a, b_times_c_over_d);

    Of course, you can name these variables whatever you like, or not use them at all. Lua is rather flexible in that case.

    Logical Operations​

    Much like arithmetic operations, logical operations also have their own helper functions. Unlike arithmetic operations however, the helper functions are your only option when it comes to logical operations.

    Code:
    gt(a: Array<number>, b: Array<number>) -> Array<bool>
    💬 Will return true in places where a > b.

    ⚠️ a and b must be of the same length.

    Code:
    lt(a: Array<number>, b: Array<number>) -> Array<bool>
    💬 Will return true in places where a < b.

    ⚠️ a and b must be of the same length.

    Code:
    geq(a: Array<number>, b: Array<number>) -> Array<bool>
    💬 Will return true in places where a >= b.

    ⚠️ a and b must be of the same length.

    Code:
    leq(a: Array<number>, b: Array<number>) -> Array<bool>
    💬 Will return true in places where a <= b.

    ⚠️ a and b must be of the same length.

    Code:
    eq(a: Array<number|bool>, b: Array<number|bool>) -> Array<bool>
    💬 Will return true in places where a == b.

    ⚠️ a and b must be of the same length.

    Code:
    neq(a: Array<number|bool>, b: Array<number|bool>) -> Array<bool>
    💬 Will return true in places where a != b.

    ⚠️ a and b must be of the same length.

    Code:
    land(a: Array<bool>, b: Array<bool>) -> Array<bool>
    💬 Will return true in places where a && b.

    ⚠️ a and b must be of the same length.

    Code:
    lor(a: Array<bool>, b: Array<bool>) -> Array<bool>
    💬 Will return true in places where a || b.

    Code:
    lif(cond: Array<bool>, a: Array<number|bool>, b: Array<number|bool>) -> Array<number|bool>
    💬 Will return an array with values from a wherever cond is true and values from b wherever cond is false

    ⚠️ cond, a, and b must be of the same length.

    Code:
    lnot(a: Array<bool>) -> Array<bool>
    💬 Inverts a, turning true to false and vice versa.

    Variable Initialization​

    Sometimes you may need to initialize your own variables. Since most variables are expected to be arrays, initializing them manually would be time-consuming and not very performant.

    However, Naxbot R provides you with an easy and performant way to do so, using the following function:
    Code:
    constant(a: number) -> Array<number>
    💬 Returns an array filled with the number a. The length of the array depends on the number of klines.

    Performance Tips​

    All of the above operators, as well as all of Naxbot R's indicators are implemented natively in Rust, meaning they are highly performant by default. However, it also depends on you to write performant code. For example, if you want to get the exponential moving average of the kline's close normally and multiplied by 5, you can do either of the following:
    Lua:
    local ema21 = ema(close, 21);
    local ema21mul = ema(close, 21) * constant(5);
    -- do some math with ema21 and ema21mul...
    or:
    Lua:
    local ema21 = ema(close, 21);
    local ema21mul = ema21 * constant(5);
    -- do some math with ema21 and ema21mul...

    Both pieces of code do the same, but they don't perform the same. Since in the first example, ema(close, 21) is called twice, that piece of code will take longer to run. As such, if you know you will be reusing the results of some calculation, you should aim to store it in a local variable, to avoid recalculating it multiple times!
Top