如何更新趋势线的价格和时间坐标
创建一条趋势线后,工作通常还没结束。我们常常需要更新线条的位置,让它在新的价格K线形成时保持最新。这一节来看看如何把趋势线移动到新的价格和时间坐标。
line.set_xy1()与line.set_xy2()
我们用line.new()函数创建趋势线。指标或策略执行该函数时,TradingView在图表上的起点和终点之间画一条线。但线的位置并非一成不变,有几种方法可以重新定位它。
一个选项是同时更改线条的时间和价格坐标,也就是把线的起点或终点移动到新的X轴(时间)坐标和不同的Y轴(价格)坐标。Pine Script有两个函数负责这件事:
- line.set_xy1():更改该线第一个点的价格(Y轴)和时间(X轴)坐标。
- line.set_xy2():更新该线第二个点的价格和时间坐标。
这两个函数需要三条信息:要更改的线、新的时间坐标和新的价格坐标。默认语法格式如下:
line.set_xy1(id, x, y)
line.set_xy2(id, x, y)
id参数指定要修改哪条线,用的是创建线条时line.new()返回的线条引用;x参数设置新的时间坐标,可以是K线编号或时间值(下文会解释);y参数定义新的价格坐标。
顺带说一句,移动趋势线还有三种其他方式:只更新线的价格坐标、只更改线的时间坐标,以及把线的坐标类型在K线编号和时间值之间切换。
那么更新时间坐标时,该用K线编号还是时间值?答案取决于该线当前使用的坐标类型:线用K线编号定位时,新位置也必须用K线编号设置,这些值通过bar_index变量获取;线用时间值定位时,就用时间值指定新位置,常见的获取方式是time和time_close变量。
如果不确定你的线用的是哪种类型,那它多半用的是K线编号,因为这是默认设置。只有代码里显式用了xloc.bar_time,线条才会使用时间值。
还有一个重要限制要注意:当线使用K线编号时,line.set_xy1()和line.set_xy2()无法把线的位置更新到当前K线之后。也就是说,用K线编号可以把线移到更早的K线(如bar_index[10])或当前K线(bar_index),但不能超出当前K线。
这个限制不适用于时间值。通过时间坐标,我们可以把线移到当前K线的右侧——用时间值可以自己算出线条应该落在的未来时间点。要让趋势线使用时间值,要么创建时把line.new()的xloc参数设为xloc.bar_time,要么用line.set_xloc()函数更改已有线条的坐标类型。
快速示例
K线编号和时间值各看一个例子。要把线的第一个点重新定位到不同的K线编号,可以这样用line.set_xy1():
// 创建一条趋势线
myLine = line.new(x1=bar_index[15], y1=close[15],
x2=bar_index, y2=close)
// 更新该线的第一个点
line.set_xy1(id=myLine, x=bar_index[3], y=high[3])
这段代码先用line.new()创建一条连接15根K线前收盘价和当前K线的趋势线,返回的引用存入myLine变量。
然后更改线的第一个点:调用line.set_xy1()并传三个参数——第一个指定要改的线(myLine变量);第二个是新的时间坐标,设为3根K线前的K线编号(bar_index[3]);第三个是新的价格坐标,用3根K线前的高点(high[3])。
当然,也可以把线的端点更新到一个时间值。代码如下:
// 创建一条趋势线
myLine = line.new(x1=time_close[10], y1=close[10],
x2=time_close, y2=close, xloc=xloc.bar_time)
// 将该线的第二个点更新到当前收盘价之上0.5%
// 且在当前K线之后4小时处
line.set_xy2(id=myLine, x=time + 14400000,
y=close * 1.005)
这段代码先用line.new()创建一条连接10根K线前收盘价和当前收盘价的趋势线,时间坐标用K线的收盘时间(time_close),并声明该线使用时间值(xloc=xloc.bar_time)。
然后用line.set_xy2()更新线的第二个点,同样是三个参数:第一个指定改哪条线(myLine);第二个是新的时间坐标,取当前K线开盘时间(time)加上4小时对应的毫秒数;第三个是新的价格坐标,设为当前收盘价上浮0.5%。
顺便一提,Pine Script里关键字参数多数时候是可选的,示例里写它们只是为了清晰。下面的代码和前一个示例功能完全相同,但省掉了可选的关键字参数:
// 创建一条趋势线
myLine = line.new(time_close[10], close[10], time_close, close,
xloc=xloc.bar_time)
// 更新该线的第二个点
line.set_xy2(myLine, time + 14400000, close * 1.005)
示例指标:把线移动到双顶和双底
现在看这两个函数在完整脚本中的用法。下面的示例指标用于识别双顶和双底:当两根连续K线有相同的高点,且该高点是20周期内的最高高点时,我们认为是一个双顶;双底的识别方式类似。
为了高亮这些顶部和底部,我们用两条趋势线。线只创建一次,之后每当新的顶部或底部形成,就用line.set_xy1()和line.set_xy2()更新它们。
该指标的完整代码如下:
//@version=5
indicator(title="双顶和双底趋势线", overlay=true)
// 只创建一次趋势线
var topLine = line.new(x1=na, y1=na, x2=na, y2=na,
color=color.green, width=3, extend=extend.both)
var bottomLine = line.new(x1=na, y1=na, x2=na, y2=na,
color=color.red, width=3, extend=extend.both)
// 发现双顶和双底
doubleTop = high == high[1] and
high == ta.highest(high, 20)
doubleBottom = low == low[1] and
low == ta.lowest(low, 20)
// 将顶线更新到最近的双顶位置
if doubleTop
line.set_xy1(topLine, x=bar_index[1], y=high)
line.set_xy2(topLine, x=bar_index, y=high)
// 将底线移动到最近的双底区域
if doubleBottom
line.set_xy1(bottomLine, x=bar_index[1], y=low)
line.set_xy2(bottomLine, x=bar_index, y=low)
先用indicator()函数配置指标,然后创建两条趋势线:
// 只创建一次趋势线
var topLine = line.new(x1=na, y1=na, x2=na, y2=na,
color=color.green, width=3, extend=extend.both)
var bottomLine = line.new(x1=na, y1=na, x2=na, y2=na,
color=color.red, width=3, extend=extend.both)
第一条线用于标记双顶:设为绿色(color.green),加大宽度(width=3),并向两个方向延伸(extend=extend.both),返回的引用存入topLine变量。第二条线标记双底:红色(color.red),宽度相同,同样向两个方向延伸,引用存入bottomLine变量备用。
这里有两点值得注意。一是var关键字,带它的语句只执行一次,所以顶线和底线总共只会创建一次。二是时间和价格坐标用的是na,这个空值只是临时占位,后续代码会把线移到正确的位置。
接下来寻找双顶和双底:
// 发现双顶和双底
doubleTop = high == high[1] and
high == ta.highest(high, 20)
doubleBottom = low == low[1] and
low == ta.lowest(low, 20)
当前K线的高点和前一根K线的高点相同(high == high[1]),并且这个高点等于ta.highest()算出的20周期最高高点时,doubleTop变量为true;任一条件不满足则为false。
类似地,K线低点等于前一根K线的低点,并且该低点是ta.lowest()算出的20周期最低低点时,doubleBottom变量为true,其他情况为false。
现在可以把趋势线更新到双顶和双底的位置了。先移动双顶线:
// 将顶线更新到最近的双顶位置
if doubleTop
line.set_xy1(topLine, x=bar_index[1], y=high)
line.set_xy2(topLine, x=bar_index, y=high)
这个if语句检查doubleTop变量。为true时执行两个函数:line.set_xy1()更新线的起点——先用topLine变量指明要改的线,时间坐标设为前一根K线(bar_index[1]),价格坐标是当前K线的高点;line.set_xy2()则把终点更新到当前K线编号(bar_index)和当前高点。
看起来这只是一条很短的线——坐标只落在前一根和当前K线之间。但别忘了我们创建时就把线向两侧延伸了,所以它实际上横跨整个图表。
双底线用同样的方式更新:
// 将底线移动到最近的双底区域
if doubleBottom
line.set_xy1(bottomLine, x=bar_index[1], y=low)
line.set_xy2(bottomLine, x=bar_index, y=low)
这个if语句检查doubleBottom变量。为true时,line.set_xy1()把bottomLine的起点更新到前一根K线编号和当前低点,line.set_xy2()把终点改到当前K线编号和低点。
代码讨论完,看看脚本在图表上的效果:

可以看到,指标在最近的双顶和双底处画出了水平线。在这个欧洲斯托克50指数CFD的日内图表上,大部分近期价格行为都被捕捉在这个范围内。由于线条向两侧延伸,很容易看出哪些历史和未来的K线位于最近的双顶和双底之间。
两个函数的特性
line.set_xy1()和line.set_xy2()函数有以下特性:
- 要知道更新哪条线,两个函数都需要一个线条引用。这个值只有一种获得方式:把line.new()的返回值存进变量。没有引用,就无法更新趋势线的位置。
- 想查看线的当前时间坐标,用line.get_x1()和line.get_x2();想取价格坐标,用line.get_y1()和line.get_y2()。
- 给这两个函数传na值而不是有效的线条引用时,脚本不会报错,但线的位置也不会更新。所以如果代码不起作用,先检查线条引用是否正确。
- line.set_xy1()和line.set_xy2()可以把趋势线移到未来的位置(当前K线右侧),但前提是该线使用时间值而非K线编号。
- 用这两个函数时,时间和价格坐标必须同时更新。只想改价格坐标,用line.set_y1()和line.set_y2();只想改时间坐标,用line.set_x1()和line.set_x2()。
- line.new()创建的线无法手动修改,所以更新线的价格和时间坐标必须通过这两个函数。
- 这两个都是void函数(无返回值函数),不返回成功或失败的状态。想验证线的坐标是否真的改了,需要再去获取该线的价格或时间坐标。
总结
用代码创建趋势线后可以更改它的位置,line.set_xy1()和line.set_xy2()函数就是其中一种方式。它们分别更新线的起点和终点,各需要三个参数。
第一个是线条引用,让函数知道更新哪条线;第二个是新的时间坐标,用K线编号还是时间值取决于该线当前的坐标类型;第三个是新的价格坐标。
只更改时间坐标也可以,用line.set_x1()和line.set_x2()实现;而line.set_y1()和line.set_y2()则只更新线的价格坐标。
如何更新趋势线的时间坐标
代码画出一条趋势线后,我们常常需要更新它的位置,让线条在新的价格K线形成时保持最新。这一节来看看如何把趋势线移动到新的时间坐标,同时保持价格坐标不变。
line.set_x1()与line.set_x2()
TradingView的指标和策略用line.new()创建趋势线,每次执行都会在两点之间画一条线。但线的位置并非一成不变,我们有多种方式移动它,其中之一就是只更改线的时间坐标——把起点和/或终点移到新的X轴坐标,Y轴(价格)坐标保持原样。Pine Script用以下两个函数完成:
- line.set_x1():更新该线第一个点的时间坐标。
- line.set_x2():更改该线第二个点的时间坐标。
每个函数需要两个必需参数,默认语法格式如下:
line.set_x1(id, x)
line.set_x2(id, x)
id参数指定要修改哪条线,用的是创建线条时line.new()返回的线条引用;x参数定义新的X轴(时间)坐标,可以是K线编号或时间值(下文详述)。
顺带一提,更改线的位置还有几种其他方式:只更新线的价格、同时更改时间和价格坐标,或者把坐标类型在K线编号和时间值之间切换。
那么该用K线编号还是时间值?这取决于该线当前使用的坐标类型:线用K线编号时,新的X轴位置也必须用K线编号设置,编号通过bar_index变量获取;线用时间值时,就用时间值定义新位置,可以通过time和time_close变量获取。
TradingView的趋势线默认使用K线编号。如果不确定你的线用哪种类型,那它多半用的是K线编号——只有代码里显式用了xloc.bar_time,线条才会使用时间值。
还要注意,当线使用K线编号时,新的时间坐标应该等于或小于当前K线的索引,也就是说line.set_x1()和line.set_x2()没法把这样的线移到未来。这个限制只针对用K线编号的线;用时间值的线可以移到任何未来位置。要让趋势线使用时间值,要么创建时把line.new()的xloc参数设为xloc.bar_time,要么用line.set_xloc()修改已存在的线。
快速示例
K线编号和时间值各看一个例子。趋势线默认用K线编号,所以标准方式是把线更新到一个新的K线编号:
// 创建一条趋势线
myLine = line.new(x1=bar_index[10], y1=close[10],
x2=bar_index, y2=close)
// 更新该线的第一个点
line.set_x1(id=myLine, x=bar_index[100])
这段代码先用line.new()创建一条连接10根K线前收盘价和当前收盘价的趋势线,返回的引用存入myLine变量备用。
然后更新线第一个点的时间坐标:调用line.set_x1(),id参数用持有线条引用的myLine变量,x参数把时间坐标改到一百根K线前(bar_index[100])。
另一个选项是用时间值设置线的X轴坐标:
// 创建一条使用时间值作为X轴坐标的趋势线
myLine = line.new(x1=time[20], y1=close[20],
x2=time, y2=close, xloc=xloc.bar_time)
// 将该线的第二个点更新到
// 当前K线的收盘时间 + 1小时
line.set_x2(id=myLine, x=time_close + 3600000)
这里我们先用line.new()创建一条连接当前收盘价和20根K线前收盘价的趋势线,时间坐标用K线的开盘时间(time),并通过xloc=xloc.bar_time让线使用时间值。
然后用line.set_x2()更新线的位置:通过myLine变量提供线条引用,新的时间坐标取当前K线的收盘时间(time_close)加上一小时的毫秒数,线的第二个点就向未来移动了一小时。
要注意,line.set_x1()和line.set_x2()并不决定趋势线用K线编号还是时间值——那是line.new()(或line.set_xloc())配置的。通过这两个函数,我们只能使用该线已经在用的坐标格式。
顺便一提,Pine Script里并不总是需要关键字参数,示例中用它们只是为了清晰。下面这样写也完全有效:
// 创建一条趋势线
myLine = line.new(time[20], close[20], time, close,
xloc=xloc.bar_time)
// 更新该线的第二个点
line.set_x2(myLine, time_close + 3600000)
示例指标:价格盘整与RSI范围
了解了line.set_x1()和line.set_x2(),来看看实践用法。下面的示例指标寻找价格盘整的时期——我们把它定义为价格在最高高点和最低低点之间波动的阶段。
脚本找到这样的时期后,会在相对强弱指数(RSI)值的近期范围处画趋势线。思路是借此建立突破水平,在预期价格脱离盘整范围时或许可以交易。
这是该指标的完整代码:
//@version=5
indicator(title="示例 - 趋势线时间坐标", overlay=false)
// 只创建一次高点和低点线
var highLine = line.new(x1=bar_index[1], y1=close[1],
x2=bar_index, y2=close, color=color.green,
width=2, extend=extend.right)
var lowLine = line.new(x1=bar_index[1], y1=close[1],
x2=bar_index, y2=close, color=color.red,
width=2, extend=extend.right)
// 计算相对强弱指数 (RSI)
rsiValue = ta.rsi(close, 12)
// 寻找价格盘整的时期
priceConsol = high < ta.highest(high, 10)[1] and
low > ta.lowest(low, 10)[1]
// 获取RSI的近期极值
rsiHigh = ta.highest(rsiValue, 10)[1]
rsiLow = ta.lowest(rsiValue, 10)[1]
// 当一个新的价格盘整发生时,移动
// 该趋势线到那个位置
if priceConsol and not priceConsol[1]
// 更改该线的价格坐标
line.set_y1(highLine, y=rsiHigh)
line.set_y2(highLine, y=rsiHigh)
line.set_y1(lowLine, y=rsiLow)
line.set_y2(lowLine, y=rsiLow)
// 更新该线的时间坐标
line.set_x1(highLine, x=bar_index[1])
line.set_x2(highLine, x=bar_index)
line.set_x1(lowLine, x=bar_index[1])
line.set_x2(lowLine, x=bar_index)
// 绘制RSI及其超买和超卖水平
plot(rsiValue, title="RSI")
hline(25)
hline(75)
内容不少,我们一步步来。先用indicator()函数定义指标属性,然后创建两条趋势线:
// 只创建一次高点和低点线
var highLine = line.new(x1=bar_index[1], y1=close[1],
x2=bar_index, y2=close, color=color.green,
width=2, extend=extend.right)
var lowLine = line.new(x1=bar_index[1], y1=close[1],
x2=bar_index, y2=close, color=color.red,
width=2, extend=extend.right)
第一个line.new()调用创建了一条连接前一根K线收盘价和当前收盘价的线。这个位置只是临时的,稍后会给它正确的坐标。这条线设为绿色(color.green),比默认更粗(width=2),并无限向右延伸。返回的引用存入highLine变量备用。
第二条线几乎是完全的复制品,只是颜色改成了红色(color.red),引用存入lowLine变量。
注意两条线都用var语句创建,这意味着这两个line.new()调用只执行一次。
接着计算RSI并寻找价格盘整期:
// 计算相对强弱指数 (RSI)
rsiValue = ta.rsi(close, 12)
// 寻找价格盘整的时期
priceConsol = high < ta.highest(high, 10)[1] and
low > ta.lowest(low, 10)[1]
我们用ta.rsi()函数计算RSI,振荡器的值存入rsiValue变量。
然后定义价格盘整:高点低于(<)10周期最高高点,同时低点高于10周期最低低点,计算用的是ta.highest()和ta.lowest()函数。两个条件都满足时priceConsol变量为true(否则false)。
之后获取RSI的近期高低点值:
// 获取RSI的近期极值
rsiHigh = ta.highest(rsiValue, 10)[1]
rsiLow = ta.lowest(rsiValue, 10)[1]
在rsiValue变量上调用ta.highest()和ta.lowest()取得RSI值的近期极值,结果存入rsiHigh和rsiLow变量。提前算好,之后就能用它们更新趋势线。
然后判断是否开始了一个新的价格盘整期:
// 当一个新的价格盘整发生时,移动
// 该趋势线到那个位置
if priceConsol and not priceConsol[1]
// 更改该线的价格坐标
line.set_y1(highLine, y=rsiHigh)
line.set_y2(highLine, y=rsiHigh)
line.set_y1(lowLine, y=rsiLow)
line.set_y2(lowLine, y=rsiLow)
// 更新该线的时间坐标
line.set_x1(highLine, x=bar_index[1])
line.set_x2(highLine, x=bar_index)
line.set_x1(lowLine, x=bar_index[1])
line.set_x2(lowLine, x=bar_index)
这个if语句判断priceConsol变量在当前K线上为true、且在前一根K线上为false(not priceConsol[1])——这种情况说明一个新的盘整期刚刚开始,趋势线该更新到当前盘整的位置了。
先更新两条线的价格坐标到近期的最高和最低RSI值:调用line.set_y1()和line.set_y2()把highLine的价格值设为10周期RSI高点(rsiHigh),再对lowLine和rsiLow做同样的操作。
但这只更新了价格,时间坐标也要跟上。所以line.set_x1()和line.set_x2()用bar_index变量把highLine设置到当前和前一根K线上。虽然这只是一根K线长的线,但没关系——之前已经把趋势线向右延伸了,它会无限向右伸展。lowLine也以同样方式更新。
指标的最后一部分把RSI画成常规线图,并用hline()函数在超买(75)和超卖(25)水平画出水平线:
// 绘制RSI及其超买和超卖水平
plot(rsiValue, title="RSI")
hline(25)
hline(75)
看看这段代码在图表上的表现:

在这个富时100指数CFD图表上,价格正处于横盘整理,但RSI已经脱离了盘整开始时的范围。也就是说,价格还没选择方向,RSI却已呈现上升趋势。
两个函数的特性
line.set_x1()和line.set_x2()函数有以下特性:
- 要知道更改哪条线,两个函数都需要一个线条引用。这个值只有一种获得方式:把line.new()的返回值存进变量。没有引用,就无法更新线的时间坐标。
- 这两个函数可以把趋势线移到未来的位置(当前K线右侧),但要求该线使用时间值而非K线编号。
- 给它们传na值而不是有效的线条引用时,脚本不会报错,但线的位置也不会更新。所以如果代码不起作用,先检查线条引用是否有效。
- 代码创建的趋势线无法手动更改,所以更新线的时间坐标必须通过这两个函数。
- 这两个都是void函数(无返回值函数),完成工作后不返回任何值。想确认线是否真的移动了,用line.get_x1()和line.get_x2()查看。
总结
线创建之后仍然可以更改。一种移动方式是用line.set_x1()和line.set_x2()函数更新线的X轴(时间)坐标:line.set_x1()更改第一个点,line.set_x2()负责第二个点。
这两个函数都需要两个参数:第一个是线条引用,告诉函数改哪条线;第二个是新的时间坐标,可以是K线编号(bar_index)或时间值(如time或time_close)。
具体用哪种,取决于该线当前的坐标类型:线用K线编号,就用K线编号更新位置;线用时间值,就用时间值设置新位置。


