There is a moment every intermediate Pine Script user hits: you are tracking several things about each of something, say the price, bar index, and strength of each pivot, and you end up with three separate arrays that all have to stay in perfect sync. Update one and forget another, and everything breaks. User-defined types fix this by letting you bundle related fields into a single object.
Defining a type
You declare a user-defined type with the type keyword and list its fields, each with a type and an optional default. Once defined, you create instances with Typename.new() and access fields with dot notation. It reads much like a struct or a simple class in other languages.
//@version=6
indicator("A pivot object", overlay = true)
type Pivot
float price
int barIndex
bool isHigh
// Create an instance
p = Pivot.new(price = high, barIndex = bar_index, isHigh = true)
// Access fields with dot notation
plotchar(p.isHigh and bar_index == p.barIndex, "pv", "P", location.abovebar)Arrays of objects: the real payoff
The moment UDTs earn their keep is when you store them in an array. Instead of parallel arrays for price, bar index, and flag, you keep one array of Pivot objects. Everything about a pivot travels together, so there is no way for the fields to fall out of sync. Combined with var for persistence, covered in the var and varip guide, this is the clean way to build up history you control.
//@version=6
indicator("Track recent pivots as objects", overlay = true)
type Pivot
float price
int barIndex
var Pivot[] pivots = array.new<Pivot>()
ph = ta.pivothigh(5, 5)
if not na(ph)
array.push(pivots, Pivot.new(price = ph, barIndex = bar_index[5]))
if array.size(pivots) > 10
array.shift(pivots)
// Read the most recent pivot's price
recent = array.size(pivots) > 0 ? array.get(pivots, array.size(pivots) - 1).price : na
plot(recent, "last pivot high")Compare this to maintaining pivotPrices and pivotBars as two arrays indexed together. The object version cannot desynchronize, reads more clearly, and extends easily: add a field to the type and every stored pivot gains it. This is the same structural clarity we recommend when choosing collections in arrays, matrices, and maps.
Pairing types with maps
Objects pair especially well with maps. A map whose values are UDT instances lets you keep everything about a keyed entity in one place, for example one object per symbol in a scanner, looked up by ticker. This keeps a multi-symbol tool readable instead of spreading each symbol's data across several maps, and it connects directly to the scanner patterns in dynamic requests.
//@version=6
indicator("Per-symbol state with a map of objects")
type SymState
float lastClose
float rsi
var map<string, SymState> states = map.new<string, SymState>()
// In a real scanner you would loop symbols and fill this
map.put(states, "AAPL", SymState.new(lastClose = 100.0, rsi = 55.0))
s = map.get(states, "AAPL")
plot(na(s) ? na : s.rsi, "AAPL RSI")When to reach for a UDT
Use a user-defined type whenever an entity has more than one attribute that belongs together, and especially when you store many of them. Pivots, trade records, zones, per-symbol state: all are natural objects. For a single loose value, a UDT is overkill. The signal to reach for one is the parallel-array smell: the moment you have two or more arrays that must always be indexed in lockstep, an array of objects is the cleaner design.
Modeling data with types and objects is where Pine Script starts to feel like real software engineering, and it is also where generic AI tends to fall back on messy parallel arrays because it does not reach for the modern constructs. PineScripter writes idiomatic v6 with user-defined types where they make the code cleaner, and validates that it compiles. If you are building something stateful enough to need objects, you can model it cleanly at PineScripter without hand-writing all the boilerplate.
Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.