保持敬畏之心
交易是一场持久战

Pine Script(177):获取标签位置,get_x、get_y与坐标类型判断

#Pine Script入门教学

哪些Pine Script函数返回TradingView标签的位置

每个TradingView标签都显示在一个特定的图表位置。多数标签样式都带有一个箭头,精确地指向其X轴(时间)坐标与Y轴(价格)坐标的交点。但Pine Script是如何获取这些坐标的呢?

TradingView的指标和策略可以使用以下两个函数来获取一个标签的位置信息:

函数 描述
label.get_y() 返回标签的Y轴(价格)坐标。
label.get_x() 返回标签的X轴(时间)坐标。

我们来逐一了解每个函数的用法。

获取价格坐标:label.get_y()

label.get_y()函数返回一个标签的Y轴坐标。这个值通常是交易品种的价格,但实际上也可以是我们赋予标签的任何脚本计算值。

在函数能够返回该坐标之前,它需要知道要访问哪个标签。为了告知函数具体要访问哪个标签,我们需要向它传递一个标签标识符。这个标识符正是label.new()函数在创建标签后所返回的值。

因此,要获取一个标签的Y轴坐标,我们的步骤如下。首先,使用label.new()创建一个标签,并把其标识符存储起来:

// 在当前K线的最低价下方两个真实波幅(True Range)的距离处绘制一个标签
myLabel = label.new(x=bar_index, y=low - 2 * ta.tr, color=color.purple,
	 textcolor=color.white, size=size.large)

这里的label.new()在当前K线最低价的下方绘制了一个标签。我们把函数返回的标识符存储在myLabel变量中。

然后,我们结合这个变量使用label.get_y()来获取该标签的Y轴坐标:

// 获取标签的价格坐标
labelYCoordinate = label.get_y(myLabel)

这段代码把获取到的坐标存储在一个变量(labelYCoordinate)中。这样做通常很方便,但并非强制要求。我们也可以在需要使用Y轴坐标的地方直接调用label.get_y()

例如,要把该标签的坐标绘制成线,我们可以把label.get_y()放置在plot()函数内部:

// 在图表上绘制标签的价格坐标
plot(label.get_y(myLabel), color=color.orange, title="Label price")

一旦我们获取了一个标签的价格坐标,就有多种用途。我们可以用这个坐标作为趋势线的起点,把其数值显示在另一个标签的文本中,用它作为策略的止盈订单价格,或者用它来计算新的价格坐标。

获取时间坐标:label.get_x()

label.get_x()函数返回特定标签的X轴坐标。这个坐标既可以是K线编号,也可以是时间值。

label.get_x()具体返回哪种类型的值,取决于该标签自身的坐标模式:

  • 当标签使用K线编号时,label.get_x()返回一个K线编号。
  • 当标签使用时间值时,label.get_x()返回一个时间值。

标签默认使用K线编号,因此在大多数情况下,label.get_x()返回的是K线编号。只有当我们通过label.new()label.set_xloc()函数为标签设置了xloc.bar_time值,它才会使用时间值(此时label.get_x()也将返回一个时间值)。

为了知道要从哪个标签获取X轴坐标,label.get_x()需要一个信息:标签的标识符。这个标识符由label.new()函数在创建标签时返回。

我们来看代码示例。首先,我们创建一个标签并把其标识符存储在一个变量中:

// 在当前K线编号和最高价的位置创建一个新标签
myLabel = label.new(x=bar_index, y=high, color=color.olive,
	 textcolor=color.white, size=size.large)

此处的label.new()在当前K线的最高价创建了一个标签。我们把函数返回的标识符存入myLabel变量。

接下来,我们结合这个变量使用label.get_x()来获取标签的X轴坐标:

// 获取我们刚刚创建的标签的时间坐标
labelXCoordinate = label.get_x(myLabel)

这就把X轴坐标存储在了labelXCoordinate变量中。随后我们便可以在其他脚本逻辑中使用这个变量。

另一种方法是在需要标签X轴坐标的地方直接使用label.get_x()。例如,要把该坐标绘制出来,我们可以把label.get_x()放入plot()函数中:

// 将标签的X轴坐标作为一条实线显示在图表上
plot(label.get_x(myLabel), color=color.red, title="Label x-axis coordinate")

一个标签的X轴坐标有多种用途。这个坐标可以作为新绘图(如另一个标签或趋势线)的定位点。或者,我们可以基于其旧坐标来移动标签,例如移动到其原始位置左侧20根K线或48小时处。当标签使用时间值时,我们还可以计算出其时间坐标距离现在有多久。

总结

有两个Pine Script函数可以返回标签的图表位置。

  • label.get_y()函数返回标签的Y轴(价格)坐标。
  • label.get_x()函数告诉我们标签的X轴(时间)坐标。

这两个函数都需要一个必要参数:我们想要查询其坐标的标签的标识符。这个标识符由label.new()函数在创建标签时返回。

用label.get_y()获取标签的价格坐标

当我们的Pine Script代码创建了一个标签后,label.get_y()函数可以返回该标签在Y轴上的坐标。

图表上的每一个标签,都由一个X轴(时间)坐标和一个Y轴(价格)坐标共同定位。label.get_y()正是用于获取后者的值。获取到这个坐标值后,我们可以把它用于其他Pine Script函数,或作为计算标签新位置的依据。

要返回Y轴坐标,label.get_y()需要知道我们具体指代的是哪一个标签。

函数语法

该函数的标准语法是:

label.get_y(id)

这里的id参数指定了我们想要查询其Y轴坐标的标签的标识符。函数正是通过这个标识符来精确定位目标标签。如果缺少此参数,Pine Script将无从知晓我们的意图。这个标识符是在当初我们使用label.new()函数创建标签时,由该函数返回的。

如果id参数的值意外地为na,函数将无法在图表上找到对应的标签,因此也不会返回任何价格坐标。但值得注意的是,这种情况并不会导致脚本报错。

label.get_y()的返回值有两种可能:

  • 一个代表标签Y轴坐标的数值。
  • na值。在以下两种情况下会返回此值:我们提供给label.get_y()id本身就是na,而不是一个有效的标签标识符,由于函数无法凭一个无效的标识符定位标签,它只能返回na;或者此前的代码已把标签的Y轴坐标设置为了na,在这种情况下,label.get_y()自然也只会返回na

快速范例

在获取标签的Y轴坐标前,我们必须先创建一个标签。我们用label.new()来完成这一步:

// 在当前K线,于低点下方两倍真实波幅(TR)的位置绘制一个标签
myLabel = label.new(x=bar_index, y=low - 2 * ta.tr, color=color.purple,
	 textcolor=color.white, size=size.large)

这段代码在当前K线(由bar_index指定)上绘制了一个标签。其价格坐标位于当前K线低点(low)下方两倍真实波幅(ta.tr)的距离处。标签为紫色背景(color.purple)、白色文字(color.white),且尺寸较大(size.large)。

label.new()返回的标识符被我们存储在了myLabel变量中。接着,我们就可以使用这个变量配合label.get_y()来获取该标签的价格坐标:

// 获取刚才创建标签的价格坐标
labelYCoordinate = label.get_y(id=myLabel)

这里,我们调用label.get_y()并传入myLabel变量,函数便知道要从哪个标签获取价格坐标。返回的坐标值被我们存入了labelYCoordinate变量。

得到这个价格坐标后,我们可以做很多事。例如,让标签的文本显示它自身的价格坐标:

// 将标签的文本设置为它自身的Y轴坐标
label.set_text(id=myLabel, text=str.tostring(labelYCoordinate))

这段代码使用label.set_text()函数来修改myLabel标签的文本内容。新的文本内容就是标签自身的价格坐标(存储在labelYCoordinate变量中)。由于价格坐标是数值类型,而标签文本要求是字符串类型,我们必须使用Pine Script内置的str.tostring()函数进行转换。

图表示例

在图表上,我们刚刚完成的快速范例看起来是这样的:

下面是上述三个代码片段如何整合成一个完整脚本的:

//@version=5
indicator(title="label.get_y() 快速范例", overlay=true)

// 仅在图表的最后一根K线上执行计算
if barstate.islast

    // 在当前K线,于低点下方两倍真实波幅(TR)的位置绘制一个标签
    myLabel = label.new(x=bar_index, y=low - 2 * ta.tr, color=color.purple,
         textcolor=color.white, size=size.large)

    // 获取刚才创建标签的价格坐标
    labelYCoordinate = label.get_y(id=myLabel)

    // 将标签的文本设置为它自身的Y轴坐标
    label.set_text(id=myLabel, text=str.tostring(labelYCoordinate))

我们从indicator()函数开始,通过title为脚本命名,并通过overlay使脚本叠加在主图表上。接着,一个if语句检查当前脚本是否正在处理图表的最后一根K线(barstate.islast)。如果是,脚本便会执行我们刚才讨论过的代码:label.new()创建新标签,label.get_y()获取其价格坐标,最后label.set_text()把这个坐标设为标签的文本。

关键字参数

label.get_y()id关键字参数是可选的。我在上面的例子中使用它,是为了让代码更清晰易懂。但实际上,省略这个不必要的关键字是更常见的做法,这样可以减少一些代码输入。省略可选关键字后,label.get_y()的调用如下所示:

// 获取刚才创建标签的价格坐标
labelYCoordinate = label.get_y(myLabel)
//                            ^ 省略了可选的 'id='

label.get_y()的使用方式

label.get_y()的功能非常纯粹:返回一个标签的价格坐标。它不执行任何其他操作,也没有任何副作用。

一旦获取了标签的价格坐标,我们便可以大展拳脚。比如,我们可以用它作为另一个绘图对象(如新标签或趋势线)的坐标,把坐标值直接显示在标签内部,或者用它来计算新的价格坐标等等。

如果我们还想知道标签的时间坐标,可以使用label.get_x()函数。两者结合,就能得到标签在图表上的精确位置。

脚本范例

我们通过一个完整的脚本来看看label.get_y()的实际应用。下面的指标会在价格穿越由WMA平滑过的EMA时创建一个标签。

该标签的价格坐标被设定为这条平滑均线的值。我们使用label.get_y()来获取它的Y轴值,然后把这个值绘制在图表上。这会形成一条水平线,直观地显示出最近一次交叉发生时均线的具体价位。

指标的完整代码如下:

//@version=5
indicator(title="获取标签的价格坐标", overlay=true)

// 计算并绘制一条平滑移动平均线
wmaValue = ta.wma(ta.ema(close, 20), 3)
plot(wmaValue, color=color.teal, linewidth=2)

// 创建一个持久化的标签变量(以便绘图函数能获取到标签的价格)
var label wmaSignalLabel = na

// 当价格穿越平滑均线时,创建一个新标签
if ta.cross(close, wmaValue)
    wmaSignalLabel := label.new(bar_index, wmaValue, color=#FDD5B1)

	// 根据穿越方向设置标签文本
    if close > wmaValue
        label.set_text(wmaSignalLabel, "▲")
    else
        label.set_text(wmaSignalLabel, "▼")

// 在最新标签的价格坐标处,绘制一个圆形图
plot(label.get_y(wmaSignalLabel), style=plot.style_circles,
     color=color.orange, linewidth=3)

脚本始于indicator()函数,用于命名和设置图表叠加。接着计算平滑均线:先用ta.ema()计算20周期的EMA,再用ta.wma()对其进行3周期的平滑处理。plot()函数把这条平滑均线以青色线图的形式绘制出来。

之后,我们创建了一个持久化变量wmaSignalLabel来保存标签的标识符。这类变量的值会在K线之间持续存在,除非被重新赋值。这使得我们在任何时候都能方便地访问到最新创建的标签标识符。

接下来是创建标签的核心逻辑:

// 当价格穿越平滑均线时,创建一个新标签
if ta.cross(close, wmaValue)
    wmaSignalLabel := label.new(bar_index, wmaValue, color=#FDD5B1)

这个if语句使用ta.cross()检测收盘价是否与平滑均线发生穿越。一旦穿越,label.new()便会创建一个新标签。标签位于当前K线(bar_index)和均线值(wmaValue)处,背景色为浅杏色(#FDD5B1)。label.new()返回的标识符被赋给了wmaSignalLabel变量,以便后续引用。

然后,我们为标签设置文本:

// 根据穿越方向设置标签文本
if close > wmaValue
	label.set_text(wmaSignalLabel, "▲")
else
	label.set_text(wmaSignalLabel, "▼")

这个嵌套的if/else语句判断穿越的方向。如果收盘价在均线上方,说明是向上穿越,我们就用label.set_text()把标签文本设为上三角符号”▲”。反之,则设为下三角符号”▼”。

最后,我们把标签的价格坐标绘制在图表上:

// 在最新标签的价格坐标处,绘制一个圆形图
plot(label.get_y(wmaSignalLabel), style=plot.style_circles,
     color=color.orange, linewidth=3)

我们调用label.get_y()并传入wmaSignalLabel来获取最新标签的价格。然后,plot()函数把这个返回的价格值以橙色实心圆(plot.style_circles)的形式绘制出来,并设置了较粗的线宽。

由于我们只在价格穿越时才创建新标签,因此在两次穿越之间,标签的标识符和价格坐标都保持不变。plot()函数会持续在同一价位上绘图,直到下一次穿越发生,从而形成一条标记了最新信号触发价位的水平线。

在图表上,我们能看到每次均线穿越后都有一串水平的圆点。同时,还有根据穿越方向变化的上下三角符号标签:

label.get_y()的重要特性

尽管我们常说label.get_y()返回价格坐标,但更精确地讲,它返回的是Y轴坐标值。因为即便我们在主图表下方的指标窗口中创建标签,该函数同样能返回其在该窗口中的Y轴坐标。label.get_y()并不关心这个坐标代表的是价格、指标值还是成交量。

另外,label.get_y()返回的坐标不一定是标签在屏幕上的可见位置。当我们使用锚定功能(yloc.abovebaryloc.belowbar)时,标签会被强制显示在K线的上方或下方,而不是其数据上设定的Y轴位置。这时,label.get_y()获取的坐标可能与标签的可见位置完全不同。

例如,一个被锚定的标签,即使其Y轴坐标数据是0-1250,它依然会显示在K线的上方或下方。而label.get_y()返回的是它被设定的坐标(即0-1250),而不是它被锚定后的视觉位置。

因此,除非我们能确定某个标签没有被label.new()label.set_yloc()锚定,否则我们不应把label.get_y()的返回值视为其在屏幕上的绝对位置。

总结

  • label.get_y()函数返回指定标签的Y轴坐标。
  • 函数只需要一个参数:从label.new()获取的标签标识符。
  • 对于被锚定的标签,label.get_y()返回的是其数据坐标,而非其视觉位置。这意味着函数的返回值可能与标签在图表上实际显示的位置不同。

用label.get_x()获取标签的时间坐标

在我们的Pine Script代码创建标签之后,可以使用label.get_x()函数来返回该标签位置的X轴坐标。

Pine Script把每个标签放置在图表的特定位置。这个位置是X轴(时间)坐标与Y轴(价格)坐标的组合。

label.get_x()告诉我们的就是那个时间刻度上的坐标。然后,我们可以把这个值用于其他的Pine Script函数,或者用它来计算标签的新位置。

在返回X轴坐标之前,label.get_x()必须知道我们指的是哪个标签。我们来深入了解。

函数语法格式

该函数的标准语法格式如下:

label.get_x(id)

id参数用于指定我们想要查询其X轴坐标的标签的标识符。函数正是通过这个标识符来识别要访问哪个标签,没有标识符,Pine Script将无从知晓我们指的是哪个标签。这个标识符由label.new()函数在创建标签时返回。如果我们(不小心)为该参数传入了na值,函数将无法识别标签(因而不会返回X轴坐标),但好在这种情况下函数并不会抛出错误。

label.get_x()会返回以下三种可能的值之一:

  • 一个大于零的数值,代表标签的X轴坐标。这个数值可以是K线编号,也可以是时间值,具体取决于该标签此前的设置:当标签使用K线编号时,label.get_x()返回一个K线编号;当标签使用时间值时,label.get_x()返回一个代表时间值的数值。
  • 不确定标签使用的是哪种模式?请记住:标签默认使用K线编号,或者在使用xloc.bar_index值时也是如此。除非我们通过label.new()label.set_xloc()函数为其设置了xloc.bar_time值,否则标签使用的就是K线编号,而非时间值。
  • -1:当标签的X轴坐标本身被设置为na值时。
  • na:当我们提供给label.get_x()id标识符本身就是na值时。

快速示例

我们来看看如何使用label.get_x()。要获取一个标签的X轴坐标,我们首先需要一个标签。因此,我们先创建一个:

// 在当前K线编号和最高价的位置创建一个新标签
myLabel = label.new(x=bar_index, y=high, color=color.olive,
	 textcolor=color.white, size=size.large)

这里的label.new()在当前K线编号(bar_index)和最高价(high)处绘制了一个标签。标签颜色为橄榄色(color.olive),文字为白色(color.white),尺寸也设置为大号(size.large)。

由于我们没有使用xloc.bar_time值把标签的坐标类型设置为时间值,它会采用默认的K线编号模式。这意味着label.get_x()稍后也会返回一个K线编号。

我们把label.new()返回的标识符存入myLabel变量,以便后续通过该变量来访问此标签。

接下来,我们使用label.get_x()获取该标签的X轴坐标:

// 获取我们刚刚创建的标签的时间坐标
labelXCoordinate = label.get_x(id=myLabel)

这里我们调用label.get_x()并传入myLabel变量。这会告知函数应从哪个标签返回其X轴坐标。我们把返回的坐标存储在labelXCoordinate变量中。

现在我们知道了标签的X轴坐标,就可以用它来做很多事。例如,我们可以让标签的文本显示其自身的K线编号坐标:

// 将标签的文本设置为其自身的X轴坐标
label.set_text(id=myLabel, text=str.tostring(labelXCoordinate))

这里的label.set_text()函数用于设置myLabel标签的文本。我们从labelXCoordinate变量中获取坐标值。由于该标签使用的是K线编号,这里显示的便是标签所在的K线编号。

因为labelXCoordinate变量持有的是一个数值,而标签文本需要的是字符串(一段文本),所以我们使用str.tostring()函数把数值转换为文本。

图表示例

上述快速示例所创建的标签看起来是这样的:

以下代码展示了如何把上面的代码片段整合为一个完整的脚本:

//@version=5
indicator(title="label.get_x() 快速示例", overlay=true)

// 在图表的最后一根价格K线上,创建并修改一个文本标签
if barstate.islast

    // 在当前K线编号和最高价处创建一个新标签
    myLabel = label.new(x=bar_index, y=high, color=color.olive,
         textcolor=color.white, size=size.large)

    // 获取我们刚刚创建的标签的时间坐标
    labelXCoordinate = label.get_x(id=myLabel)

    // 将标签的文本设置为其X轴坐标
    label.set_text(id=myLabel, text=str.tostring(labelXCoordinate))

我们从indicator()函数开始,它为脚本命名并使脚本叠加显示在主图表上。

接着,一个if语句判断脚本是否正在处理最后一根价格K线(barstate.islast)。如果条件为真,指标便会执行我们前面讨论过的代码:label.new()创建一个新标签,label.get_x()获取该标签的X轴坐标,最后label.set_text()把标签的文本设置为这个坐标值。

关键字参数

label.get_x()id关键字参数是可选的。我在上面的示例中使用了它,是为了让代码更清晰易懂。但实际上,更常见的做法是省略这个关键字,这样可以使代码输入更简短。

省略可选的id关键字后,label.get_x()的调用方式如下:

// 获取我们刚刚创建的标签的时间坐标
labelXCoordinate = label.get_x(myLabel)
//                            ^ 省略了可选的 'id='

label.get_x()的使用方式

label.get_x()的功能非常专一:返回标签的X轴坐标。该函数不能做其他事情,也没有任何副作用。

一旦我们知道了标签的X轴坐标,就有多种可能性。这个坐标可以作为另一个绘图(如新标签或趋势线)的定位点;我们可以计算出标签是在多久以前创建的;或者,我们还可以基于标签的当前坐标来计算一个新的X轴坐标(例如,把标签移动到未来24小时或其原始位置右侧45根K线处)。

要获取标签的Y轴(价格)坐标,我们应使用label.get_y()函数。把label.get_x()与之结合,我们便能获知标签在图表上的确切位置。

示例脚本

我们在一个完整的TradingView脚本中使用label.get_x()。下面的指标会在价格穿越成交量加权平均价(VWAP)时创建一个标签。

这些标签不仅显示信号发生的位置,还会显示距离上一个信号经过了多少分钟,这有助于我们了解信号发生的频率。为了确定这个时间间隔,label.get_x()会检索前一个标签的X轴坐标。

该指标的完整代码如下:

//@version=5
indicator(title="获取标签的时间坐标", overlay=true)

// 计算并绘制1日VWAP值
vwapPrice = ta.vwap(hlc3)
plot(vwapPrice, color=color.fuchsia, linewidth=2)

// 创建一个持久化标签变量(用于访问之前创建的标签)
var label vwapSignalLabel = na

// 当价格穿越VWAP时,创建一个新标签
if ta.cross(close, vwapPrice)
    vwapSignalLabel := label.new(x=time, y=high, xloc=xloc.bar_time,
         color=#FBCCE7)

    // 计算当前K线与上一个标签的时间坐标之间的分钟差
    minutesSince = (time - label.get_x(vwapSignalLabel[1])) / 60000

    // 在标签文本中显示经过的分钟数
    label.set_text(vwapSignalLabel, "Minutes since\nlast signal:\n" + 
         str.tostring(minutesSince))

首先,我们调用indicator()函数,用title为脚本命名,并用overlay使其叠加显示在主图表上。

然后,ta.vwap()函数基于hlc3(高、低、收盘价的平均值)计算VWAP。我们把这个移动平均线的值存储在vwapPrice变量中。

接下来,plot()函数在图表上显示这个值。由于未设置绘图类型,Pine Script默认绘制一条常规的线图。这条线以紫红色(color.fuchsia)显示。

然后,var关键字创建了一个名为vwapSignalLabel的持久化变量。这个变量会记住(即持久化)我们赋给它的最后一个值,这使得访问前一个信号创建的标签变得很容易。

之后,我们为每个VWAP信号创建一个新标签:

// 当价格穿越VWAP时,创建一个新标签
if ta.cross(close, vwapPrice)
    vwapSignalLabel := label.new(x=time, y=high, xloc=xloc.bar_time,
         color=#FBCCE7)

这个if语句使用ta.cross()函数检查收盘价(close)是否与VWAP(vwapPrice)发生了穿越。如果价格确实上穿或下穿了该平均线,函数返回trueif语句内的代码便会执行。

这段代码首先使用label.new()创建一个新的文本标签。该标签显示在当前K线的时间戳和最高价(timehigh)处。xloc=xloc.bar_time值告知标签应使用时间值而非K线编号。标签的颜色为#FBCCE7,即古典玫瑰色的十六进制色值。

我们把label.new()返回的标签标识符存入vwapSignalLabel变量。这使得我们可以通过该变量访问当前以及前一个标签。

接下来,我们计算自上一个标签以来的时间:

// 计算当前K线与上一个标签的时间坐标之间的分钟差
minutesSince = (time - label.get_x(vwapSignalLabel[1])) / 60000

为了确定经过的分钟数,我们执行了几个步骤。首先,我们获取当前K线的开盘时间(time),然后减去前一个标签的时间值。

我们通过label.get_x()来获取那个时间坐标。这需要一个标签标识符。我们通过在vwapSignalLabel变量后使用历史操作符([])并附带数值1,来获取上一个标签的标识符。

由于我们之前创建标签时指定了使用时间值(xloc.bar_time),因此label.get_x()也会返回一个时间值。Pine Script的时间值以毫秒为单位,所以我们计算出的差值也是一个毫秒数。

然而,我们想得到的是分钟数,所以我们把计算出的时间间隔除以60000。这就得出了自上一个VWAP信号以来经过了多少分钟。

最后,我们用计算出的分钟数来更新最新标签的文本:

// 在标签文本中显示经过的分钟数
label.set_text(vwapSignalLabel, "Minutes since\nlast signal:\n" + 
     str.tostring(minutesSince))

我们调用label.set_text()为最新的标签设置文本。我们通过vwapSignalLabel变量获取该标签的标识符。

另一个参数是一个由字符串字面量与minutesSince变量(通过+连接)组成的文本。minutesSince变量里存着我们刚才计算出的分钟数。

然而,该变量持有的是一个数值,而label.set_text()需要的是文本(字符串)。为了解决这个不兼容的问题,我们使用Pine Script的str.tostring()函数把变量的值转换为文本。

在图表上,该指标会在每次价格上穿或下穿VWAP时绘制标签。每次信号发生时,标签还会告诉我们距离上一个VWAP穿越经过了多少分钟:

label.get_x()的特性

尽管我们可能会说label.get_x()返回的是时间坐标,但更准确的说法是,该函数返回的是一个X轴坐标。因为当我们的标签使用K线编号时,它的X轴坐标用的就不是时间值,而是K线编号。

总结

  • label.get_x()函数从一个特定的标签返回其X轴坐标。
  • 该函数需要一个参数:标签的标识符。我们从label.new()函数获得这个值。
  • 当标签使用K线编号时,label.get_x()也返回一个K线编号。如果标签使用时间值,那么函数会给我们一个时间值。
  • 由于标签默认使用K线编号,在大多数情况下label.get_x()返回的是K线编号。只有当我们对label.new()label.set_xloc()函数使用了xloc.bar_time值,标签才会使用时间值(此时label.get_x()也将返回一个时间值)。

检查标签使用的是K线编号还是时间值

Pine Script提供了两种在图表上定位文本标签的方式:我们可以使用K线编号或时间值。目前,并没有内置函数能够直接告知我们某个标签究竟使用的是哪种方式。但了解这一点非常有帮助,例如,当我们需要更新一个标签的位置时,就必须根据该标签当前使用的坐标类型来决定是设置K线编号还是时间值。

幸运的是,我们可以编写一个自定义函数来判断标签使用的是K线编号还是时间值。不过在此之前,我们先来探讨一下这两种X轴坐标有何不同。

K线编号与时间值

在Pine Script中,我们通过K线编号或时间值来指定绘图对象(如标签、趋势线和方框)的X轴坐标。这两种值都是整数,但它们的取值范围差异巨大。

K线编号从0开始,一直到大约20,000(TradingView图表上K线的最大数量)。而时间值衡量的是自1970年以来的毫秒数,因此它们的数值要大得多。

如果我们追溯到几十年前的某个随机日期,比如1997年9月3日,那一天的时间值就已经高达873,241,200,000。更近的日期则拥有更大的时间值,例如2021年9月10日的时间值为1,631,232,000,000。

由于时间值如此之大(相比之下K线编号则很小),我们可以制定一套规则来判断一个X轴坐标是K线编号还是时间值:

  • 如果一个绘图对象的X轴坐标在0到100万之间,那么它使用的是K线编号。
  • 当其X轴坐标超出该范围时,它使用的是时间值。

这个100万的阈值水平大约是TradingView图表上K线数量上限的50倍。设置如此高的值有两个原因。

首先,这能使我们的代码在未来多年保持稳健,因为在此期间TradingView很可能会增加图表所能容纳的K线数量。其次,我们之所以能奢侈地选择一个高阈值,是因为时间值的量级仍然比它大得多。

上述规则并非100%可靠。在一个极小的时间窗口内,它们无法准确判断一个绘图使用的是K线编号还是时间值。这个窗口从1970年1月1日0:00:00 UTC开始,到同一天的0:16:40 UTC结束。

如果我们的脚本恰好在这16分钟的窗口内创建了一个绘图,我们将无法判断该标签使用的是K线编号还是时间值。

但这是可以接受的。在该时间窗口内创建绘图的概率极小。大多数图表(尤其是日内图)的历史数据不会追溯到1970年。而像加密货币这样的某些交易品种在那时甚至还不存在。所以,我们在Pine Script代码中实现这套规则吧。

自定义函数

下面的自定义函数可以判断指定的标签为其X轴坐标使用的是哪种类型。当标签使用K线编号时,该函数返回xloc.bar_index值;如果绘图使用时间值,则返回xloc.bar_time值。

函数的代码如下:

// GetLabelXloc() 函数用于获取指定标签的X轴坐标类型。
// 返回两个可能的常量之一:
// - xloc.bar_index (表示标签使用K线编号作为其时间坐标)
// - xloc.bar_time (表示标签使用时间值作为其时间坐标)
// 注意:对于1970年1月1日0:00:00至0:16:40 UTC之间的K线,
// 函数结果可能不准确。
GetLabelXloc(labelId) =>
    timeCoordinate = label.get_x(labelId)

    if timeCoordinate >= 0 and timeCoordinate <= 1000000
        xloc.bar_index
    else
        xloc.bar_time

这个GetLabelXloc()函数接受一个参数:labelId。它指定了我们想要查询其X轴定位方式的标签的标识符。

在函数内部,我们首先使用label.get_x()获取该标签的X轴坐标。

接着,一个if/else语句评估该坐标值是否落在我们定义的K线编号范围内。如果该值大于或等于(>=)0并且(and)小于或等于(<=)100万,那么条件成立。在这种情况下,函数返回xloc.bar_index常量。否则,else代码块将返回xloc.bar_time常量。

xloc.bar_indexxloc.bar_time这两个值可能看起来有些陌生,但使用它们并不困难。

假设我们创建了一个文本标签,并把其标识符存储在myLabel变量中。要检查该标签是否使用K线编号,我们可以在代码中这样写:

// 检查标签是否使用K线编号
usesBars = GetLabelXloc(myLabel) == xloc.bar_index

而要测试该标签是否使用时间值,我们可以这样使用这个自定义函数:

// 检查标签是否使用时间值
if GetLabelXloc(myLabel) == xloc.bar_time
	// ...

示例脚本

我们通过一个完整的脚本来看看自定义函数GetLabelXloc()是如何工作的。下面的指标会检查一个标签使用的是K线编号还是时间值。如果它使用K线编号,相应的文本会显示在标签上;如果绘图使用时间值,标签也会告知这一信息。

完整的代码如下:

//@version=5
indicator(title="查看标签是使用K线编号还是时间值", overlay=true)

// GetLabelXloc() 函数用于获取指定标签的X轴坐标类型。
// 返回两个可能的常量之一:
// - xloc.bar_index (表示标签使用K线编号作为其时间坐标)
// - xloc.bar_time (表示标签使用时间值作为其时间坐标)
// 注意:对于1970年1月1日0:00:00至0:16:40 UTC之间的K线,
// 函数结果可能不准确。
GetLabelXloc(labelId) =>
    timeCoordinate = label.get_x(labelId)

    if timeCoordinate >= 0 and timeCoordinate <= 1000000
        xloc.bar_index
    else
        xloc.bar_time

// 创建一个输入选项,以便在K线编号和时间值之间轻松切换
labelCoordinateStyle = input.string("bar numbers", 
     title="标签坐标样式", options=["bar numbers", "time values"])

// 在最后一根已确认的历史K线上,创建一个示例标签
if barstate.islastconfirmedhistory
    // 首先,根据输入选项将值转换为标签可以使用的形式
    xAxisValue    = labelCoordinateStyle == "bar numbers" ? bar_index : time
    xAxisLocation = labelCoordinateStyle == "bar numbers" ? 
         xloc.bar_index : xloc.bar_time

    // 创建一个文本标签
    myLabel = label.new(x=xAxisValue, y=close, style=label.style_label_left,
         textcolor=color.white, size=size.large, xloc=xAxisLocation)

    // 如果标签使用时间值,就在标签文本中说明;否则
    // 告知标签使用的是K线编号
    labelText = if GetLabelXloc(myLabel) == xloc.bar_time
        "标签使用\n时间值"
    else
        "标签使用\nK线编号"

    label.set_text(id=myLabel, text=labelText)

    // 如果标签使用K线编号,将其颜色设为紫红色;否则设为栗红色
    if GetLabelXloc(myLabel) == xloc.bar_index
        label.set_color(id=myLabel, color=color.fuchsia)
    else
        label.set_color(id=myLabel, color=color.maroon)

首先,我们使用indicator()函数来配置指标设置,它为脚本命名并使脚本叠加显示在主图表上。

然后,我们引入了上面讨论过的自定义GetLabelXloc()函数。

指标的其余代码可分为三个部分:首先是处理输入选项的代码,然后我们创建一个标签,最后测试该标签使用的是K线编号还是时间值。我们逐一解析。

我们先来创建一个自定义的输入设置:

// 创建一个输入选项,以便在K线编号和时间值之间轻松切换
labelCoordinateStyle = input.string("bar numbers", 
     title="标签坐标样式", options=["bar numbers", "time values"])

这里Pine Script的input.string()函数创建了一个字符串输入选项。该输入的默认值为"bar numbers"

title参数为该输入命名,这会在脚本的设置界面中,在该输入控件前显示”标签坐标样式”的文字。

通过options参数,我们定义了该设置的可用值,即bar numbers和time values。这会创建一个包含这两个预设值的下拉菜单。输入的默认值(defval)是bar numbers。

我们把输入控件的当前值存储在labelCoordinateStyle变量中。后续代码访问此变量时,将获取到用户在设置中选择的值。(如果我们更改输入设置,该变量的值也会随之更新。)

接下来,一个if语句评估barstate.islastconfirmedhistory变量。该变量仅在图表的最后一根已确认的历史K线上为true(这是一种仅执行一次代码的便捷技巧)。在该K线上,我们首先创建一个文本标签:

// 首先,根据输入选项将值转换为标签可以使用的形式
xAxisValue    = labelCoordinateStyle == "bar numbers" ? bar_index : time
xAxisLocation = labelCoordinateStyle == "bar numbers" ? 
	 xloc.bar_index : xloc.bar_time

// 创建一个文本标签
myLabel = label.new(x=xAxisValue, y=close, style=label.style_label_left,
	 textcolor=color.white, size=size.large, xloc=xAxisLocation)

这里,我们首先根据用户的输入来确定标签的设置。为此,我们创建了两个变量。

xAxisValue变量用于保存标签的X轴坐标。当用户输入选项(labelCoordinateStyle)设为bar numbers(K线编号)时,它保存一个K线编号;否则,它保存一个时间值(time)。xAxisLocation变量则用于指定坐标的类型。如果标签使用K线编号,其值为xloc.bar_index;如果使用时间值,则为xloc.bar_time

准备好这两个变量后,我们便使用label.new()来创建标签。其价格坐标设为最后一个历史K线的收盘价(close)。时间坐标则使用xAxisValue变量,该变量的值由用户输入动态决定。

标签的样式被设为指向左侧(label.style_label_left)且尺寸较大(size.large)。其X轴的定位模式由xAxisLocation变量控制,这使得标签能够根据输入选项的设置,灵活地采用时间值或K线编号进行定位。

label.new()返回的标签标识符被存放在myLabel变量中,以便我们后续可以引用和修改这个标签。

现在,我们根据标签实际使用的坐标类型来动态调整其格式:

// 如果标签使用时间值,就在标签文本中说明(反之
// 则告知其使用的是K线编号)
labelText = if GetLabelXloc(myLabel) == xloc.bar_time
	"标签使用\n时间值"
else
	"标签使用\nK线编号"

label.set_text(id=myLabel, text=labelText)

// 如果标签使用K线编号,则将其设为紫红色,否则设为栗红色
if GetLabelXloc(myLabel) == xloc.bar_index
	label.set_color(id=myLabel, color=color.fuchsia)
else
	label.set_color(id=myLabel, color=color.maroon)

我们首先定义了labelText变量。为了给它赋值,一个if/else语句评估了GetLabelXloc()函数的返回值。如果该函数针对myLabel标签返回的值等于xloc.bar_time,我们就知道该标签正在使用时间值定位,并相应地设置labelText变量的文本内容。如果GetLabelXloc()返回了其他值,那必然意味着标签使用的是K线编号(因为没有其他选项)。

因此,else块内的代码会赋予变量相应的说明文本。最后,我们调用label.set_text(),把标签的文本更新为labelText变量的值。

接下来是另一个if/else语句。它用于测试GetLabelXloc()的返回值是否等于xloc.bar_index。如果相等,说明标签是按K线编号定位的。在这种情况下,我们使用label.set_color()把标签背景色设为紫红色(color.fuchsia)。否则,当标签使用时间值时,else块内的代码则把其颜色设为栗红色(color.maroon)。

代码已经足够多了,我们看看脚本的运行效果。当我们把指标添加到图表时,由于输入项的默认值,它会使用K线编号。因此,标签显示为紫红色,并提示其使用的是K线编号:

如果我们修改输入选项,让标签改用时间值,标签的颜色和文本会随之改变,提示它现在使用的是时间值:

其他可行方法

上面我们创建的自定义函数并非判断标签坐标类型的唯一途径。但该函数确实有几个优点:

  • 功能单一:仅用一个函数,既能测试K线编号也能测试时间值。
  • 返回值复用:函数返回的值也可用于其他场景。(例如,我们可以获取标签的X轴定位模式,并在创建方框(box)绘图时复用该模式。)

话虽如此,我们可能并不需要GetLabelXloc()函数返回的额外信息。我们看看另外两种方法。

如果我们只想简单地判断一个标签是否使用K线编号作为其时间坐标,可以使用以下函数:

// 如果给定的标签使用K线编号 (xloc.bar_index) 作为其时间坐标,
// LabelUsesBarNumbers() 函数返回 true。如果标签是按时间值定位,
// 则返回 false。
// (注意:对于1970年1月1日 UTC 时间 0:00:00 至 0:16:40 之间的
// K线,此函数的结果可能不准确。)
LabelUsesBarNumbers(labelId) =>
    timeCoordinate = label.get_x(labelId)
    timeCoordinate >= 0 and timeCoordinate <= 1000000

如果指定的标签使用K线编号,这个LabelUsesBarNumbers()函数就会返回true。(反之,如果使用时间值,则返回false。)我们可以这样使用该函数:

// 检查 'myLabel' 标签是否使用K线编号
usesBars = LabelUsesBarNumbers(myLabel)  // 返回 true 或 false

// 或者在条件语句中直接使用:
if LabelUsesBarNumbers(myLabel)
	// ...

当然,我们也可以反过来测试标签是否使用时间值。下面是实现该功能的函数:

// 如果给定的标签使用时间值 (xloc.bar_time) 作为其时间坐标,
// LabelUsesTimeValues() 函数返回 true。如果标签是按K线编号定位,
// 则返回 false。
// (注意:对于1970年1月1日 UTC 时间 0:00:00 至 0:16:40 之间的
// K线,此函数的结果可能不准确。)
LabelUsesTimeValues(labelId) =>
    timeCoordinate = label.get_x(labelId)
    timeCoordinate < 0 or timeCoordinate > 1000000

如果指定的标签使用时间值作为其X轴坐标,这个LabelUsesTimeValues()函数就会返回true。(如果使用的是K线编号,则返回false。)我们可以这样来使用这个函数:

// 检查 'myLabel' 标签是否使用时间值
usesTimes = LabelUsesTimeValues(myLabel)  // 返回 true 或 false

// 同样可以在条件语句中使用:
if LabelUsesTimeValues(myLabel)
	// ...
赞(0)
未经允许不得转载:图道交易 » Pine Script(177):获取标签位置,get_x、get_y与坐标类型判断
分享到

评论 抢沙发

登录

找回密码

注册