如何用代码删除趋势线
一个TradingView指标或策略创建的趋势线,只要脚本处于活动状态就会一直留在图表上。但如果我们创建了一条现在想移除的趋势线,该怎么办呢?这一节来看看具体做法。
line.delete()的用法
TradingView脚本用line.new()函数创建趋势线。在每一根执行该函数的K线上,TradingView都会在两个价格和时间坐标之间画出一条线。
但并非每条趋势线都应该永久留在图表上。比如,我们可能希望每次画新线时都把前一条移掉,这正是line.delete()函数的功能。它只需要一个必需参数,语法格式如下:
line.delete(id)
id参数指定要从图表上移除哪条线,这里用的值是一个线条引用,也就是创建线条时line.new()返回的那个值。
快速示例:移除前一条线
要移除一条趋势线,我们调用line.delete()并在括号内指明要删哪条线。下面是一种用法:
// 创建一个持久化变量来持有线条的标识符
var line myLine = na
// 寻找一个更高的收盘价
if close > close[10]
// 首先删除前一条线
line.delete(id=myLine)
// 然后绘制一条新线
myLine := line.new(x1=bar_index[10], y1=close[10],
x2=bar_index, y2=close)
这里我们先创建了一个持久化(var)变量,用来存储线条的标识符,这样才能访问在之前K线上创建的线。
然后if语句判断当前收盘价是否高于(>)10根K线前的收盘价。如果是,我们先执行line.delete(),用myLine变量指定要移除哪条线——变量里当前存着谁的标识符,谁就被从图表上删掉。
接着用line.new()创建一条新线,连接10根K线前的收盘价和当前K线,并把返回的标识符存回myLine变量。等下一次if条件再成立时,被line.delete()删掉的就是这条线。
再看另一种使用line.delete()的方式:
var line myLine = na
// 判断本周是否刚刚开始
if dayofweek == dayofweek.monday
// 创建一条新的趋势线
myLine := line.new(x1=bar_index[10], y1=close[10],
x2=bar_index, y2=close)
// 移除前一条线
line.delete(id=myLine[1])
同样先创建一个持久化变量来持有线条标识符。然后if语句判断当前K线是否是星期一(dayofweek.monday)。如果是,先用line.new()画一条新趋势线,连接10根K线前的收盘价和当前K线,引用存入myLine变量。
接着用line.delete()移除前一条趋势线。注意这里不能用myLine的当前值——我们刚把新线的引用存了进去,用当前值删的就是新线本身。但通过历史引用操作符([]),我们可以拿到前一根K线上这个变量的值,所以myLine[1]删除的正是前一条线。
顺便一提,TradingView函数并不总是需要关键字参数。示例里写关键字参数只是为了清晰,省略它们也完全有效,还能少打些字:
// 创建一条新的趋势线
myLine = line.new(bar_index[10], close[10], bar_index, close)
// 移除前一条线
line.delete(myLine[1])
需要总是自己移除趋势线吗
代码能用line.delete()移除趋势线,但是否总应该这样做?要看情况。
当有特定场景需要移除某条趋势线时,line.delete()能让我们精确控制哪条线何时消失。比如在每根K线上都创建新线,但每次都删掉前一条,从而保证图表上最多只有一条趋势线。
不过在TradingView中,其实不用刻意管理图表上的趋势线数量,因为平台本身就对绘图数量设了上限。一旦达到限制,TradingView会自动移除最旧的绘图。所以一个脚本根本没法堆出几百条趋势线,即使不删旧线,图表也不会被线条淹没。这种情况下就不必专门写line.delete()去清理。
line.delete()几个值得注意的行为
line.delete()有几个特性值得指出,其中一些和其他编程语言相比会显得有点奇怪,提前了解能少走弯路。
第一,对一条已经被移除的线再次调用line.delete(),不会产生错误。这意味着代码可以放心地调用它,不用跟踪线条是否已被删除。
第二,给line.delete()传一个na值也不会报错。所以对一个还没持有线条引用的变量使用它,同样安全。这使得检查na值的if语句变得多余。换句话说,我们不需要这样写:
// 检查趋势线ID是否不等于`na`是没有必要的;
// `line.delete()`对这些值不会报错
if not na(myTrendLine[1])
line.delete(id=myTrendLine[1])
而是直接调用line.delete(),不必费心检查na值:
// 直接删除趋势线;不必费心检查
// 'myTrendLine[1]'可能存在的'na'值
line.delete(id=myTrendLine[1])
但要注意,na值确实会让line.delete()删不掉你想删的线。所以如果代码不报错、趋势线却也没被移除,就检查一下线条引用是否正确。
第三,line.delete()可以在创建趋势线后立即把它删掉,这会给人一种脚本不工作的错觉。例如:
// 创建一条新趋势线并将其引用存储在'myLine'中
myLine = line.new(x1=bar_index[20], y1=close[20],
x2=bar_index, y2=close)
// ...
// 使用'myLine'的当前值来指定要移除哪条线
line.delete(id=myLine)
这里我们用line.new()画了一条新线,引用存入myLine变量,然后用该变量的当前值告诉line.delete()删哪条线——结果删掉的就是刚创建的这条。如果你想删的是前一条趋势线,要么在创建新线之前调用line.delete(),要么用历史引用操作符明确访问前一条线(即myLine[1])。
第四个特性最奇特:虽然line.delete()会把线从图表上移除,但它并不会删除这条趋势线的对象。所以尽管我们再也看不到这条线,却仍然可以访问它。看个例子:
//@version=5
indicator(title="快速示例 - line.delete()", overlay=true)
// 在每根K线上创建一条趋势线
myLine = line.new(x1=bar_index[20], y1=close[20],
x2=bar_index, y2=close)
// 删除在前一根K线上创建的趋势线..
line.delete(id=myLine[1])
// .. 然后绘制那条'被删除'的线的价格
plot(series=line.get_y2(id=myLine[1]))
这个脚本在每根K线上创建一条新线,同时用line.delete()删除前一条(myLine[1]),然后用line.get_y2()去取那条已被移除的线的价格坐标。按直觉它应该返回0或na——线都没了嘛。但实际上它照样返回该线的价格,我们把这个值画成了一条常规的线图。
示例指标:只保留一条前收盘价线
来看line.delete()在完整脚本中的用法。假设我们交易一个24小时开放的品种,但仍希望看到常规交易时段(假设在17:00结束)的前一收盘价。一个办法是每天在那个收盘价处画一条水平线。
当然,我们不希望图表上堆满前几天的旧线。所以每次画新线时,就用line.delete()移除前一条,最终图表上只保留一条线。
这是该指标的完整代码:
//@version=5
indicator(title="前收盘价线", overlay=true)
line closeLine = na
// 寻找在17:00开盘的K线
if hour == 17 and minute == 0
// 创建一条趋势线
closeLine := line.new(x1=bar_index[1], y1=close[1],
x2=bar_index, y2=close[1], extend=extend.both,
color=color.orange, width=2)
// 移除前一条线
line.delete(closeLine[1])
首先用indicator()函数定义指标设置,然后创建线条变量:
line closeLine = na
我们将把线条的标识符(也叫引用)存在这个变量里。之所以要提前声明,是因为如果不这样做,Pine Script会在if语句内部引用该变量历史值时发出警告。
接下来寻找在17点开盘的K线:
// 寻找在17:00开盘的K线
if hour == 17 and minute == 0
// 创建一条趋势线
closeLine := line.new(x1=bar_index[1], y1=close[1],
x2=bar_index, y2=close[1], extend=extend.both,
color=color.orange, width=2)
这个if语句检查两个条件:当前K线的小时(hour)是否等于(==)17,并且(and)分钟(minute)是否等于(==)0。所有在17:00开盘的K线都满足这两个条件。
找到这样的K线后,我们用一条线高亮前一根K线的收盘价:调用line.new(),从前一根K线的收盘价(bar_index[1]、close[1])画到当前K线上的同一收盘价(bar_index、close[1])。
这样画出来的线只有一根K线长,很难看清,所以我们让它向两个方向延伸(extend=extend.both),颜色设为橙色(color.orange),宽度稍微加大(width=2)。line.new()返回的引用存入closeLine变量。
然后删除前一条趋势线:
// 移除前一条线
line.delete(closeLine[1])
用line.delete()删线时,我们通过变量加历史引用操作符(closeLine[1])拿到上一次创建的那条线的引用。
注意,即使前一根K线上并没有创建趋势线也没关系,closeLine[1]仍然能返回前一条趋势线的引用。原因是绘图变量会在每根K线上重复自己的值——不管线是5根、20根还是100根K线前创建的,只要期间没更新过closeLine变量,它就一直持有前一条线的引用。
这个指标在图表上的效果如下:

这个比特币图表上有一条水平线,高亮显示了17:00的收盘价。图表历史上其实有很多这样的收盘价,但只显示了一条线,因为我们每次创建新线时都把前一条移除了。
line.delete()函数的特性
line.delete()函数有以下特性:
- line.delete()只能移除当前脚本创建的趋势线,拿不到其他脚本创建的或手动画的趋势线,也无法访问在其他图表上创建的线。
- 要知道删哪条线,line.delete()需要一个线条引用。这个值只有一种获得方式:把line.new()的返回值存进变量。没有这个引用,就无法从图表上移除那条趋势线。
- line.new()创建的趋势线无法手动修改,所以line.delete()是移除代码所画趋势线的唯一方式。
- line.delete()是一个void函数(无返回值函数),运行后不返回任何东西。目前没有办法验证一个绘图是否真的已从图表上移除。
总结
TradingView脚本用line.new()创建趋势线。线条创建之后有多种处理方式,删除就是其中之一。
我们用line.delete()函数移除趋势线。它只需要一个参数:指定要删哪条线的线条引用,这个值在创建线条时从line.new()获得。
并非总有必要自己动手删线。TradingView本身就限制了图表上的绘图数量,太旧的绘图会被自动移除。
如何获取趋势线的价格坐标
代码创建一条趋势线之后,还有几种方式可以继续处理它,其中一个选项就是获取这条线当前的价格坐标。这一节来看看怎么实现。
line.get_y1()与line.get_y2()
TradingView的指标和策略用line.new()函数创建趋势线。执行该函数时,TradingView会在图表上的两点之间画一条线,每个点都是时间值和价格值的组合。
通常,当我们需要重新定位一条线时,会想先知道它当前的坐标。Pine Script里有两个函数可以返回线条的价格:
- line.get_y1():返回该线第一个点的价格值。
- line.get_y2():返回该线第二个点的价格值。
每个函数都需要一个参数,默认语法格式如下:
line.get_y1(id)
line.get_y2(id)
id参数指定要从哪条线获取价格坐标,这里用的值是一个线条引用,也就是创建线条时line.new()返回的值。除了价格,线条的时间坐标也可以获取,我们在讲时间坐标的章节再展开。
快速示例
下面是用line.get_y1()和line.get_y2()获取线条价格的快速示例:
// 创建一条新的趋势线
myLine = line.new(x1=bar_index[10], y1=close[10],
x2=bar_index, y2=close)
// ...
// 获取该线的价格坐标
startPrice = line.get_y1(id=myLine)
endPrice = line.get_y2(id=myLine)
这段代码先用line.new()创建一条趋势线,返回的线条引用存入myLine变量,之后我们就有办法访问这条线了。
然后获取该线的价格:调用line.get_y1()和line.get_y2(),都用myLine变量指明要取哪条线的价格,返回值分别存入startPrice和endPrice变量。
拿到线的价格后能做的事情很多,比如基于当前的价格坐标把线更新到一个新位置。
顺便一提,TradingView并不总是要求关键字参数。示例里写关键字参数只是为了清晰,下面这样写也完全有效:
// 创建一条新的趋势线
myLine = line.new(bar_index[10], close[10], bar_index, close)
// 获取该线的价格坐标
startPrice = line.get_y1(myLine)
endPrice = line.get_y2(myLine)
示例策略:在入场价处画线并跟踪盈亏
现在看看line.get_y1()和line.get_y2()在完整脚本中的用法。下面的示例策略在价格穿越20周期指数移动平均线(EMA)时交易。持仓期间,我们在策略的入场价位画一条趋势线。
持仓时,我们把K线收盘价和那条线的价格值做比较,这一步就用line.get_y2()函数完成。当K线价格跌破入场价时,我们更改线条的样式。
该策略的完整代码如下:
//@version=5
strategy(title="用趋势线跟踪入场价", overlay=true)
// 计算策略值
maValue = ta.ema(close, 20)
// 设置交易入场条件
enterLong = ta.crossover(close, maValue)
enterShort = ta.crossunder(close, maValue)
// 提交入场订单
if enterLong
strategy.entry("EL", strategy.long)
if enterShort
strategy.entry("ES", strategy.short)
var line entryLine = na
// 判断策略头寸的变化
wentLong = strategy.position_size > 0 and strategy.position_size[1] <= 0
wentShort = strategy.position_size < 0 and strategy.position_size[1] >= 0
// 当策略开仓时创建一条新线
if wentLong or wentShort
entryLine := line.new(x1=bar_index[1], y1=strategy.position_avg_price,
x2=bar_index, y2=strategy.position_avg_price, width=5)
if wentLong
line.set_color(entryLine, color=color.navy)
else
line.set_color(entryLine, color=color.fuchsia)
// 只要策略有持仓,就管理
// 该线的样式和长度
if strategy.position_size != 0
if close > line.get_y2(entryLine)
line.set_style(entryLine, style=line.style_solid)
else
line.set_style(entryLine, style=line.style_dotted)
line.set_x2(entryLine, x=bar_index)
首先用strategy()函数设置策略的选项,然后用ta.ema()计算20周期EMA。
通过ta.crossover()和ta.crossunder()函数,我们判断价格是否上穿或下穿了均线,真/假结果存入enterLong和enterShort变量。接着用if语句检查这两个变量,一旦为true,strategy.entry()就开一个多头(strategy.long)或空头(strategy.short)头寸。
之后进入趋势线相关的代码。先创建趋势线变量:
var line entryLine = na
entryLine变量的默认值是na,这个空值只是临时占位。为什么要先创建它?因为稍后我们要在if语句里创建趋势线,如果变量也在那里才声明,它的作用域就仅限于那个if语句,在if代码块之外就用不了了。提前在一行未缩进的代码里创建变量,脚本的任何地方都能访问它。
接下来判断策略是不是刚刚开了多仓或空仓:
// 判断策略头寸的变化
wentLong = strategy.position_size > 0 and strategy.position_size[1] <= 0
wentShort = strategy.position_size < 0 and strategy.position_size[1] >= 0
当策略开了多头或空头头寸时,我们画一条高亮入场价的趋势线:
// 当策略开仓时创建一条新线
if wentLong or wentShort
entryLine := line.new(x1=bar_index[1], y1=strategy.position_avg_price,
x2=bar_index, y2=strategy.position_avg_price, width=5)
if wentLong
line.set_color(entryLine, color=color.navy)
else
line.set_color(entryLine, color=color.fuchsia)
这个if语句检查策略是刚开多仓(wentLong)还是刚开空仓(wentShort),任一发生(or)就用line.new()创建一条新趋势线。
代码在前一根K线(bar_index[1])和当前K线(bar_index)之间画线,价格设为当前头寸的平均入场价(strategy.position_avg_price)。两个端点价格相同,所以得到一条水平线。line.new()返回的引用存入entryLine变量备用。
接着用嵌套的if/else语句设置线的颜色:开多仓时(wentLong),line.set_color()把线设为海军蓝(color.navy);开空仓则设为紫红色(color.fuchsia)。
不过,只在开仓时画一条线还不够,持仓期间还得持续管理这条线。这是接下来的部分:
// 只要策略有持仓,就管理
// 该线的样式和长度
if strategy.position_size != 0
if close > line.get_y2(entryLine)
line.set_style(entryLine, style=line.style_solid)
else
line.set_style(entryLine, style=line.style_dotted)
line.set_x2(entryLine, x=bar_index)
这个if语句判断策略的头寸大小(strategy.position_size)是否不等于(!=)零,持有多仓或空仓时都成立。
在这种情况下,嵌套的if/else把当前K线收盘价(close)和线条第二个点的价格坐标做比较,后者通过line.get_y2()获取。由于我们画的是水平线,这条线任何一个点的价格都代表整条线的价格。
当K线价格高于该线时,我们把线变为实线:调用line.set_style(),用entryLine变量指定目标线条,样式用line.style_solid。当K线收盘于该线或其下方时,else代码块运行,同一个函数把线变为点线(line.style_dotted)。
最后我们调用line.set_x2()更新线条第二个点的时间坐标到当前K线编号(bar_index),让线条随着K线一根根向右延长。这只在有持仓时(strategy.position_size != 0)发生。
这个策略在图表上的效果如下:

该策略为多头画蓝线,为空头画紫红线。价格在入场价之上时线条为实线——对多头来说实线意味着盈利,对空头则意味着亏损;价格跌到入场价之下时,线条变为点状。
两个函数的特性
line.get_y1()和line.get_y2()函数有几个值得注意的特性:
- 要知道从哪条线取价格,这两个函数都需要一个线条引用。这个值只有一种获得方式:把line.new()的返回值存进变量。没有引用,就无法访问趋势线的价格。
- 给line.get_y1()或line.get_y2()传na值时,TradingView不会报错,但函数也取不到线条价格。所以如果代码不报错但也不起作用,检查一下线条引用是否持有有效值。
- line.get_y1()和line.get_y2()只返回线的价格。取时间坐标要用line.get_x1()和line.get_x2();而line.set_y1()和line.set_y2()则用于更新线的价格。
- line.new()创建的趋势线无法手动交互,所以这两个函数是读取线条价格的唯一方式。
- line.get_y1()和line.get_y2()以浮点值序列的形式返回价格坐标,即使线条当初是用整数价格定位的也一样。
总结
趋势线出现在图表上之后,有几种方式可以获取它的数据,line.get_y1()和line.get_y2()就是其中之一,它们返回线的Y轴值。
line.get_y1()告诉我们线条起点的价格坐标,line.get_y2()则负责线条的终点。
这两个函数都需要一个参数:指定要取哪条线价格的线条引用。没有这个引用,我们无法从趋势线中获取价格值。


