枚举
介绍
Pine Script™ 枚举,也称为枚举、枚举类型或 枚举类型,是一种独特的数据类型,其所有可能的值(成员)均由程序员明确定义。它们提供了一种人性化、富有表现力的方式来声明变量、条件表达式和 集合可以接受的不同预定义值集,从而可以更严格地控制脚本逻辑中使用的值。
声明枚举
要声明枚举,请使用 enum 关键字,语法如下:
枚举中的每个字段都代表枚举类型的唯一命名成员(值)。用户可以为枚举字段指定可选的“常量字符串”标题,以添加有关其值所代表内容的额外信息。如果程序员未指定字段的标题,则其标题是其名称的“字符串”表示形式。 枚举输入在脚本的“设置/输入”选项卡的下拉菜单中显示枚举字段标题。脚本还可以使用 str.tostring () 函数检索枚举字段标题,从而允许将其用于其他计算。 有关更多信息,请参阅下面的部分。
虽然上述语法看起来类似于声明 用户定义类型 (UDT)的语法,但了解 枚举类型和 UDT 的用途不同至关重要。脚本使用 UDT 创建具有“系列”字段的对象,这些字段可以保存任何指定类型的值。相比之下,枚举是不同的“简单”字段组,表示变量、表达式和 集合可以接受的相同唯一类型的特定预定义值。
例如,此代码块声明了一个Signal
具有三个字段的枚举:
buy
、sell
和。每个字段代表枚举类型neutral
的一个不同成员(可能的值):Signal
//@enum An enumeration of named values representing buy, sell, and neutral signal states.
//@field buy Represents a "Buy signal" state.
//@field sell Represents a "Sell signal" state.
//@field neutral Represents a "neutral" state.
enum Signal
buy = "Buy signal"
sell = "Sell signal"
neutral
注意:
- 标识符
Signal
代表枚举的名称,表明字段所属的唯一类型。 - 我们使用
//@enum
和//@field
注释来记录枚举及其字段的含义。 buy
与和字段不同sell
,neutral
字段不包含指定标题。因此,其标题是其名称的“字符串”表示(“中性”)。
要检索枚举的成员,请使用点符号语法引用其字段名称,即:
enumName.fieldName
与其他类型一样,脚本可以将枚举成员分配给变量、函数参数和 UDT 字段,从而严格控制它们的允许值。
例如,以下代码行声明了一个mySignal
变量,其值是枚举neutral
的成员Signal
。 稍后分配给此变量的任何值也必须是相同的
枚举类型:
mySignal = Signal.neutral
请注意,上面这一行不需要将变量的
类型声明为,Signal
因为编译器可以根据赋值自动推断出该信息。如果我们改用
na作为初始值,则必须使用Signal
type 关键字来指定mySignal
将接受Signal
成员:
Signal mySignal = na
使用枚举
脚本可以使用==和 !=运算符比较枚举成员 ,并在条件结构 中使用它们 ,从而可以方便地创建逻辑模式,同时降低出现意外值或操作的风险。
以下示例声明了一个OscType
枚举,其中三个字段代表不同的振荡器选择:rsi
、mfi
和cci
。该
calcOscillator()
函数使用switchOscType
结构中的成员
来确定要计算哪个振荡器。脚本使用枚举输入
中的值
作为函数的参数来调用此函数,并绘制生成的振荡器:selection
//@version=5
indicator("Using enums demo")
//@enum An enumeration of oscillator choices.
enum OscType
rsi = "Relative Strength Index"
mfi = "Money Flow Index"
cci = "Commodity Channel Index"
//@variable An enumerator (member) of the `OscType` enum.
OscType oscInput = input.enum(OscType.rsi, "Oscillator type")
//@function Calculates one of three oscillators based on a specified `selection`.
//@param source The series of values to process.
//@param length The number of bars in the calculation.
//@param selection Determines which oscillator to calculate.
calcOscillator(float source, simple int length, OscType selection) =>
result = switch selection
OscType.rsi => ta.rsi(source, length)
OscType.mfi => ta.mfi(source, length)
OscType.cci => ta.cci(source, length)
// Plot the value of a `calcOscillator()` call with `oscInput` as the `selection`.
plot(calcOscillator(close, 20, oscInput))
注意:
selection
该函数的参数只能calcOscillator()
取四个值之一:OscType.rsi
、OscType.mfi
、OscType.cci
或 na。- 脚本“设置/输入”选项卡中的“振荡器类型”输入
OscType
在其下拉列表中显示所有字段标题。请参阅 本节以了解有关枚举输入的更多信息。
需要注意的是,每个声明的枚举都代表一个唯一的 类型。脚本无法比较不同枚举的成员,也无法在需要特定 枚举类型的表达式中使用此类成员,即使字段具有相同的名称和标题。
在此示例中,我们OscType2
向上述脚本添加了一个枚举,并将oscInput
变量更改为使用该枚举的成员。该脚本现在引发编译错误,因为它无法使用枚举的成员
OscType2
作为selection
调用中的参数calcOscillator()
:
//@version=5
indicator("Incompatible enums demo")
//@enum An enumeration of oscillator choices.
enum OscType
rsi = "Relative Strength Index"
mfi = "Money Flow Index"
cci = "Commodity Channel Index"
//@enum An enumeration of oscillator choices. Its fields DO NOT represent the same values those in the `OscType` enum.
enum OscType2
rsi = "Relative Strength Index"
mfi = "Money Flow Index"
cci = "Commodity Channel Index"
//@variable An enumerator (member) of the `OscType2` enum.
OscType2 oscInput = input.enum(OscType2.rsi, "Oscillator type")
//@function Calculates one of three oscillators based on a specified `selection`.
//@param source The series of values to process.
//@param length The number of bars in the calculation.
//@param selection Determines which oscillator to calculate.
calcOscillator(float source, simple int length, OscType selection) =>
result = switch selection
OscType.rsi => ta.rsi(source, length)
OscType.mfi => ta.mfi(source, length)
OscType.cci => ta.cci(source, length)
// Plot the value of a `calcOscillator()` call with `oscInput` as the `selection`.
// Raises a compilation error because only members of `OscType` are allowed.
plot(calcOscillator(close, 20, oscInput))
利用字段标题
枚举字段的“字符串”标题允许程序员为每个成员添加额外信息。调用 input.enum() 函数时,这些字段标题会显示在脚本“设置/输入”选项卡的下拉选项卡中。
脚本还可以在计算和逻辑中使用枚举字段标题。对枚举字段使用字符串转换函数 ( str.tostring() ) 来访问其标题。
以下示例组合了不同的枚举字段标题,以构造股票代码 ID,用于从
另一个上下文请求数据。该脚本声明了两个枚举,Exchange
和Pair
,其各自的字段代表交易所和货币对名称。它使用
input.enum()
将用户指定的枚举成员分配给exchangeInput
和
变量,然后使用str.tostring()pairInput
从这些变量中检索“字符串”标题
,并将它们连接起来形成“Exchange:Symbol”对,以用于 request.security
()
调用:
//@version=5
indicator("Utilizing field titles demo")
//@enum An enumeration of cryptocurrency exchanges. All field titles are the same as the field names.
enum Exchange
BINANCE
BITSTAMP
BITFINEX
COINBASE
KRAKEN
//@enum An enumeration of cryptocurrency pairs. All the field titles are the same as the field names.
enum Pair
BTCUSD
ETHUSD
SOLUSD
XRPUSD
//@variable An enumerator (member) of the `Exchange` enum.
Exchange exchangeInput = input.enum(Exchange.BINANCE, "Exchange")
//@variable An enumerator (member) of the `Pair` enum.
Pair pairInput = input.enum(Pair.BTCUSD, "Pair")
//@variable The exchange-symbol pair for the data request.
simple string symbol = str.tostring(exchangeInput) + ":" + str.tostring(pairInput)
// Plot the `close` value requested from the `symbol` context.
plot(request.security(symbol, timeframe.period, close), "Requested close", color.purple, 3)
注意:
Exchange
或枚举的成员均未Pair
指定标题。因此,每个字段的标题都是其名称的“字符串”表示,如脚本的 枚举输入所示。-
对枚举字段调用
str.tostring()函数是检索其标题以进行其他计算的
唯一方法。str.format ()
和
log.*()
函数不能接受枚举成员。要在字符串格式化函数中使用字段的标题,请先对字段调用 str.tostring() ,然后将生成的“字符串”传递给函数。
收集枚举成员
Pine Script™ 集合、 矩阵和 映射可以存储枚举成员,从而严格控制它们可以包含的值。要声明枚举成员的集合,请在集合的 类型模板中包含枚举的名称。
例如,此代码块创建一个空
数组
来保存枚举成员FooBar
。此数组可以允许作为元素的唯一值是FooBar.foo
、FooBar.bar
、FooBar.baz
和
na:
//@variable An enumeration of miscellaneous named members.
enum FooBar
foo
bar
baz
//@variable An array that can only contain the following values: `FooBar.foo`, `FooBar.bar`, `FooBar.baz`, `na`.
array<FooBar> fooBarArray = array.new<FooBar>()
枚举在使用映射时特别有用 ,因为与其他非基本 类型不同 ,脚本可以使用枚举类型的 键声明映射,从而能够严格控制其键值对中允许的所有可能的键。
以下示例使用
带有枚举键和“int”值的映射
来跟踪和计数图表条中的信号状态。脚本的Signal
枚举包含五个字段,代表特定的命名状态。signalCounters
映射
使用Signal
名称作为其
类型模板中的第一个关键字来指定它只能接受成员作为键。Signal
该脚本使用
switch
结构来计算一个signalState
变量,该变量的值是枚举的成员Signal
,它使用该变量来确定要在映射中更新的计数器值。它构造一个“字符串”来表示映射signalCounters
的键值对
,并在
最后一个图表栏上的
单个单元格
表中显示结果:
//@version=5
indicator("Collecting enum members demo", overlay = true)
//@enum An enumeration of named signal states.
enum Signal
strongBuy = "Strong buy"
buy = "Buy"
neutral = "Neutral"
sell = "Sell"
strongSell = "Strong sell"
//@variable The number of bars in the signal calculation.
int lengthInput = input.int(50, "Length", 2)
//@variable A map of `Signal.*` keys and "int" values counting the number of bars with each signal state.
// Allowed keys: `Signal.strongBuy`, `Signal.buy`, `Signal.neutral`, `Signal.sell`, `Signal.strongSell`, `na`.
var map<Signal, float> signalCounters = map.new<Signal, float>()
//@variable A single-cell table displaying the key-value pairs of the `signalCounters` map.
var table infoTable = table.new(position.top_right, 1, 1, chart.fg_color)
if barstate.isfirst
// Put `Signal.*`-"int" pairs into the `signalCounters` map to establish insertion order.
signalCounters.put(Signal.strongBuy, 0)
signalCounters.put(Signal.buy, 0)
signalCounters.put(Signal.neutral, 0)
signalCounters.put(Signal.sell, 0)
signalCounters.put(Signal.strongSell, 0)
// Initialize the `infoTable` cell.
infoTable.cell(0, 0, text_color = chart.bg_color, text_halign = text.align_left, text_size = size.large)
// Calculate the EMA and Percent rank of `source` data over `length` bars.
float ema = ta.ema(close, lengthInput)
float rank = ta.percentrank(close, lengthInput)
//@variable A `Signal` member representing the current signal state based on `ema` and `rank` values.
Signal signalState = switch
close > ema => rank > 70 ? Signal.strongBuy : rank > 50 ? Signal.buy : Signal.neutral
close < ema => rank < 30 ? Signal.strongSell : rank < 50 ? Signal.sell : Signal.neutral
=> Signal.neutral
// Add 1 to the value in the `signalCounters` map associated with the `signalState` key.
signalCounters.put(signalState, signalCounters.get(signalState) + 1)
// Update the `infoTable` cell's text using the keys and values from the `signalCounters` map on the last bar.
if barstate.islast
string tableText = ""
for [state, count] in signalCounters
tableText += str.tostring(state) + ": " + str.tostring(count) + "\n"
infoTable.cell_set_text(0, 0, str.trim(tableText))
注意:
- 该
signalCounters
映射最多可包含六个键值对,因为Signal
枚举具有五个预定义值,加上可能的值 na,并且映射不能包含重复的键。 - 该脚本
signalCounters
使用 var 关键字声明变量,表示分配的 映射 实例在执行过程中持续存在。 - 在第一个图表栏上,脚本使用了五个
map.put()
调用来建立地图中键的
插入顺序
signalCounters
。 有关更多信息,请参阅地图页面的此部分。 - 为了最大限度地减少资源使用,脚本
在第一个柱状图
infoTable
上声明并初始化其单元格,然后在最新的柱状图上更新单元格的文本。请参阅 分析和优化页面的 此部分以了解更多信息。
阴影
为了避免将来添加到 Pine Script™ 的命名空间与现有脚本中的枚举名称发生冲突的潜在冲突,枚举名称可以遮蔽一些 Pine 的命名空间。
例如,可以声明如下所示的枚举,其名称覆盖syminfo.*
命名空间:
//@version=5
indicator("Shadowing demo")
enum syminfo
abcd
log.info(str.tostring(syminfo.abcd))
但是,仅当枚举的字段没有与命名空间的任何内置项匹配的名称时,才允许对枚举使用这样的名称。否则,Pine 将无法确定脚本应该使用哪个值,从而导致编译错误:
//@version=5
indicator("Name conflict demo")
enum syminfo
abcd
tickerid // This matches the built-in `syminfo.tickerid` variable, causing a compilation error.
log.info(str.tostring(syminfo.tickerid))
此外,不能使用任何 Pine 的内置 类型名称作为枚举的名称。