Objects
Notice! This page contains advanced material. If you are a beginner LipiScript programmer, we recommend you become familiar with other, more accessible LipiScript features before you venture here.
Introduction
LipiScript objects are instances of user-defined types (UDTs). They are the equivalent of variables containing parts called fields, each able to hold independent values that can be of various types.
Experienced programmers can think of UDTs as methodless classes. They allow users to create custom types that organize different values under one logical entity.
Creating objects
Before an object can be created, its type must be defined. The User-defined types section of the Type system page explains how to do so.
Let’s define a pivotPoint type to hold pivot information:
type pivotPoint {
int x
float y
string xloc = xloc.bar_time
}
Note that:
We use the type keyword to declare the creation of a UDT. We name our new UDT pivotPoint. After the first line, we create a local block containing the type and name of each field. The x field will hold the x-coordinate of the pivot. It is declared as an “int” because it will hold either a timestamp or a bar index of “int” type. y is a “float” because it will hold the pivot’s price. xloc is a field that will specify the units of x: xloc.bar_index or xloc.bar_time. We set its default value to xloc.bar_time by using the = operator. When an object is created from that UDT, its xloc field will thus be set to that value. Now that our pivotPoint UDT is defined, we can proceed to create objects from it. We create objects using the UDT’s new() built-in method. To create a new foundPoint object from our pivotPoint UDT, we use:
foundPoint = pivotPoint.new();
We can also specify field values for the created object using the following:
foundPoint = pivotPoint.new(time, high);
Or the equivalent:
foundPoint = pivotPoint.new((x = time), (y = high));
At this point, the foundPoint
object’s x field will contain the value of the time built-in when it is created, y will contain the value of high, and the xloc field will contain its default value of xloc.bar_time
since no value was defined for it when creating the object.
Object placeholders can also be created by declaring na
object names using the following:
pivotPoint foundPoint = na
When declaring a variable with the static
keyword and assigning it to an object of a user-defined type, the keyword is automatically applied to all fields within the object.
indicator("Objects using `static` demo")
//@type A custom type to hold index, price, and volume information.
type BarInfo {
int index = bar_index
float price = close
float vol = volume
}
//@variable A `BarInfo` instance whose fields persist through all iterations, starting from the first bar.
BarInfo firstBar = BarInfo.new()
//@variable A `BarInfo` instance declared on every bar.
BarInfo currentBar = BarInfo.new()
// Plot the `index` fields of both instances to compare the difference.
plot(firstBar.index)
plot(currentBar.index)
Note that assigning an object to a variable declared with the intra
keyword does not automatically ensure that the object’s fields persist without resetting on each intrabar update. To achieve this behavior, the intra
keyword must be applied to each specific field in the type declaration. For example:
indicator("Objects using `intra` fields demo")
//@type A custom type that counts the bars and ticks in the script's execution.
type Counter {
int bars = 0
intra int ticks = 0
}
//@variable A `Counter` object whose reference persists throughout all bars.
static Counter counter = Counter.new()
// Add 1 to the `bars` and `ticks` fields. The `ticks` field is not subject to rollback on unconfirmed bars.
counter.bars += 1
counter.ticks += 1
// Plot both fields for comparison.
plot(counter.bars, "Bar counter", color.blue, 3)
plot(counter.ticks, "Tick counter", color.purple, 3)
Note:
- We used the
static
keyword to ensure that theCounter
object assigned to the counter variable persists throughout the script’s execution. - The bars field resets on real-time bars, while the ticks field does not, as
intra
was included in its declaration.
Changing Field Values
The value of an object’s fields can be modified using the :=
reassignment operator.
this line:
foundPoint = pivotPoint.new(time[legsInput], pivotHighPrice);
Could be written using the following:
foundPoint = pivotPoint.new()
foundPoint.x := time[legsInput]
foundPoint.y := pivotHighPrice
Copying Objects
In LipiScript, objects are assigned by reference. When an existing object is assigned to a new variable, both variables refer to the same object.
In the example below, we create a pivot1
object and set its x
field to 1000
. Next, we declare a pivot2
variable that contains a reference to the pivot1
object, meaning both point to the same instance. Therefore, changing pivot2.x
will also update pivot1.x
, as both refer to the x
field of the same object:
indicator("")
type pivotPoint {
int x
float y
}
pivot1 = pivotPoint.new()
pivot1.x := 1000
pivot2 = pivot1
pivot2.x := 2000
// Both plot the value 2000.
plot(pivot1.x)
plot(pivot2.x)
To create a copy of an object that is independent of the original, we can use the built-in copy() method.
In this example, we declare the pivot2
variable, which refers to a copied instance of the pivot1
object. Now, changing pivot2.x
will not affect pivot1.x
, as it refers to the x
field of a separate object:
indicator("")
type pivotPoint {
int x
float y
}
pivot1 = pivotPoint.new()
pivot1.x := 1000
pivot2 = pivot1
pivot2.x := 2000
// Both plot the value 2000.
plot(pivot1.x)
plot(pivot2.x)
It’s important to note that the built-in copy() method produces a shallow copy of an object. If an object contains fields with special types (such as array, matrix, map, line, linefill, box, polyline, label, table, or chart.point), those fields in a shallow copy of the object will reference the same instances as in the original.
Shadowing
To prevent potential conflicts in the event that namespaces are added to LipiScript in the future—possibly colliding with user-defined types (UDTs) or object names in existing scripts—UDTs and object names shadow the language’s namespaces.
However, the language’s five primitive types cannot be used to name UDTs or objects: int, float, string, bool, and color.