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:
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:
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:
-- 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:
add(a: Array<number>, b: Array<number>) -> Array<number>
Adds
a
and
b
together.
a
and
b
must be of the same length.
sub(a: Array<number>, b: Array<number>) -> Array<number>
Subtracts
b
from
a
.
a
and
b
must be of the same length.
mul(a: Array<number>, b: Array<number>) -> Array<number>
Multiplies
a
and
b
.
a
and
b
must be of the same length.
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:
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:
-- 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:
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;
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:
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:
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.
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.
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.
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.
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.
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.
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.
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.
lor(a: Array<bool>, b: Array<bool>) -> Array<bool>
Will return
true
in places where
a || b
.
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.
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:
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:
local ema21 = ema(close, 21);
local ema21mul = ema(close, 21) * constant(5);
-- do some math with ema21 and ema21mul...
or:
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!