自定义类型与枚举
用户定义类型(UDT)
使用 struct 关键字定义自定义类型。字段必须有显式类型,可以有可选的默认值:
struct Order {
id: int,
symbol: string,
price: float = na,
varip updateCount: int = 0,
}字段可以使用:
- 显式类型(必需)
- 可选的默认值,使用
= - 可选的
varip声明模式(使字段在 K 线内更新间持久化)
创建实例
使用 .new() 创建实例:
let myOrder: Order = Order.new(id: 1, symbol: "AAPL");访问和修改字段
let orderPrice = myOrder.price;
myOrder.price = 155.0;
myOrder.quantity = 200;复制
let clone: Order = Order.copy(myOrder);
clone.price = 160.0;类型上的方法
struct Position {
entry: float,
size: float,
isLong: bool = true,
}
method pnl(self: Position, currentPrice: float) {
let diff = currentPrice - self.entry;
self.isLong ? diff * self.size : -diff * self.size;
}
method isProfit(self: Position, currentPrice: float) {
self.pnl(currentPrice) > 0;
}
let pos = Position.new(entry: 100.0, size: 10.0);
if pos.isProfit(close) {
label.new(bar_index, high, str.tostring(pos.pnl(close), "#.##"));
}varip 用于类型字段
单个字段可以使用 varip 在 K 线内更新间持久化,而其他字段仍然会回滚:
struct Counter {
bars: int = 0,
varip ticks: int = 0,
}
var counter: Counter = Counter.new();
counter.bars += 1; // 在未确认 K 线上会回滚
counter.ticks += 1; // 不会回滚泛型 UDT
UDT 可以声明一个或多个类型参数。实例化时,参数名代表任意具体类型:
struct Pair<A, B> {
first: A,
second: B,
}
let p: Pair<int, string> = Pair.new(first: 42, second: "hello");字段类型本身也可以含有类型参数,例如持有 T 的集合:
struct Stack<T> {
items: Array<T>,
count: int = 0,
}
method push<T>(self: Stack<T>, value: T) {
self.items.push(value);
self.count += 1;
}
method pop<T>(self: Stack<T>): T {
self.count -= 1;
self.items.pop();
}
var s: Stack<float> = Stack.new();
s.push(close);
s.push(open);编译器会对每种实例化进行单态化处理——Stack<float> 和 Stack<int> 会生成独立的、类型正确的对象,引用类型字段的 GC 追踪也会自动设置。
枚举
使用 enum 关键字定义枚举。枚举变体是简单的名称(没有关联数据):
enum Direction {
Long,
Short,
Both = "Both Directions",
}使用枚举
通过枚举名称访问变体:
let d: Direction = Direction.Long;
if d == Direction.Long {
strategy.entry("L", Direction.Long);
} else if d == Direction.Short {
strategy.entry("S", Direction.Short);
}导入和导出
导出
使用 export 标记函数、类型和枚举,使它们对导入模块可见:
library("MyLib");
export struct Config {
length: int,
multiplier: float,
}
export fn calcSMA(src: series float, length: simple int) {
ta.sma(src, length);
}
export enum Side {
Left,
Right,
}导入
indicator("My Indicator");
use MyLib as lib;
let config = lib.Config.new(length: 20, multiplier: 2.0);
let sma = lib.calcSMA(close, config.length);
plot(sma);通过模块名称访问导出的成员:
use utils;
utils.add(1, 2); // 调用导出的函数
let obj: utils.MyType = na; // 使用导出的类型
let value = utils.MyEnum.A; // 访问导出的枚举变体新类型声明
Navi 支持 type Name = underlying_type; 语法来创建由现有类型支持的不同类型。这允许在不创建新结构的情况下定义特定领域的类型:
type MyInt = int;
let a: MyInt = 10; // 接受底层类型的值新类型可以分层:
type MyInt = int;
type SpecialInt = MyInt;
let x: SpecialInt = 42; // OK编译器特殊类型
以下类型由 Navi 标准库用于实现某些高级内置函数。通常情况下,你不会在自己的脚本中直接使用它们——它们出现在标准库源码中,当你调用 input.source、request.security 或 max_bars_back 等函数时会被透明处理。
expression
允许函数接收未求值的 AST 节点而非计算后的值。input.source() 用它来获知用户传入的是哪个变量,从而在 UI 中以变量名显示。
let src = input.source(defval: close);
// `close` 被捕获为表达式,不会被求值为数字。
// 设置 UI 会将"Close"显示为默认来源选项。instructions<T>
允许函数接收已编译的代码块,并在不同上下文中重新执行。request.security() 用它在不同标的或时间周期中运行表达式。
// 简单序列——每根 bar 在 AAPL/1D 上下文中重新求值:
let aapl_close = request.security("AAPL", "1D", close);
// 复合表达式——整个表达式在上下文中重新执行:
let aapl_sma = request.security("AAPL", "1D", ta.sma(close, 14));variableref
允许函数接收变量或序列的编译期引用。编译器在编译时解析该引用,并将变量的元数据传递给原生函数。max_bars_back() 用它为特定序列扩展历史缓冲区。
// 为内置序列扩展历史缓冲区:
max_bars_back(close, 500);
// 或为用户自定义序列变量:
let myAvg: series float = ta.sma(close, 20);
max_bars_back(myAvg, 200);
