Introduction
A type is a struct you define: a named bundle of fields, optionally with methods that operate on those fields. Instead of tracking a supply zone as three loose parallel arrays (zoneTops, zoneBottoms, zoneTouches), you describe it once as a Zone and work with whole zones. State takes the shape of the problem.
type Zone {
top: number
bottom: number
touches: number
}Each field is declared with a name and a type. Once a type exists, you create instances of it, read and write their fields, and pass them around like any other value.
Construct with named fields
You build an instance with .new(...), naming each field you set:
var z = Zone.new(top=d.high, bottom=d.low, touches=0)
var h = z.top - z.bottomNamed-field construction is order-independent and self-documenting: there is no "which positional argument was bottom again?" The constructor only accepts fields the type actually declares. Pass a field the type does not have and it is a compile error, not a silent extra property, so a typo is caught before the script ever runs.
Add behavior with methods
A type can carry methods. Declare them with func inside the type block, and they can read and mutate the instance through this:
type Zone {
top: number
bottom: number
touches: number
func height() {
return this.top - this.bottom
}
func registerTouch(price) {
if (price >= this.bottom && price <= this.top) {
this.touches += 1
return true
}
return false
}
}Now the data and the logic that maintains it live together. height() derives a value from the fields. registerTouch(price) is a self-updating operation: ask the zone whether the current price touched it, and it answers while bumping its own touches counter. The zone manages its own state.
var inZone = z.registerTouch(d.close) // true/false, && z.touches updates itself
var tall = z.height()This is why user-defined types matter for trading scripts. A "supply zone that counts its own touches and retires after the third," an "order block that tracks whether price mitigated it," a "trailing-stop level that ratchets": each becomes one type whose methods enforce its rules, instead of bookkeeping smeared across the whole script. Hold these in a var to track one, or in an array (see Collections) to manage a live set of them across bars.
A complete typed struct
This script declares Zone with both methods, constructs it with named fields, calls height() and registerTouch(), and lets the method mutate touches through this.
//@version=2
define(title="Verified Struct Methods", position="offchart", axis=true)
type Zone {
top: number
bottom: number
touches: number
func height() {
return this.top - this.bottom
}
func registerTouch(price) {
if (price >= this.bottom && price <= this.top) {
this.touches += 1
return true
}
return false
}
}
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var top = trade.high
var bottom = trade.low
var closeNow = trade.close
var zone = Zone.new(top=top, bottom=bottom, touches=0)
var touched = zone.registerTouch(closeNow)
var score = zone.height() + zone.touches + (touched ? 1 : 0) + barIndex
plotLine(value=score, colors=["#2563eb"], label=["Zone score"], desc=["struct method score"])What to expect: zone.height() returns the high-minus-low band width, registerTouch(closeNow) reports whether the close fell inside the band and increments zone.touches when it did, and zone.touches reads the running count back. The whole zone is one value carrying its own data and behavior.
Rules and gotchas
A few constraints the compiler enforces. Each fails at compile time with a precise line and column, so you find out immediately.
Constructors reject unknown fields. .new(...) only accepts fields the type declares. Passing one it does not have, such as Zone.new(top=..., bottom=..., label=...), fails with:
Unknown field 'label' for type 'Zone'
this only works inside methods. It refers to the current instance, so it is meaningless outside a type's own func. Using it at the top level fails with:
'this' can only be used inside type methods
__type and __kscriptType are reserved field names. The engine uses them internally to tag instances, so you cannot declare a field with either name. Doing so fails with reserved field name '__type' or reserved field name '__kscriptType'. Any other field name is yours to use.