获取自上次开仓以来的K线数
有时,策略需要知道距离上一次开仓过去了多少根K线。例如,我们可能希望对仓位进行金字塔式加仓,但又不想在每一根连续的K线上都进行交易。如果我们知道距离上一次开仓过了多少根K线,我们就可以在连续的加仓订单之间保持一个固定的K线间隔。那么,我们该如何获取这个距离上次开仓的K线数呢?
自定义函数
要计算出距离上一次开仓过去了多少根K线,需要两个要素:首先,获取当前的K线编号;然后,获取当前持仓中最后一笔订单的入场K线编号。用前者减去后者,我们就能得到想要的答案。
下面是我们如何通过一个自定义函数来实现它:
// BarsSinceLastEntry() 函数返回自策略当前持仓中
// 最后一笔、即最近一笔订单入场以来的K线数。
// 当策略没有持仓时,返回 'na'。
BarsSinceLastEntry() =>
bar_index - strategy.opentrades.entry_bar_index(strategy.opentrades - 1)
在这个 BarsSinceLastEntry() 函数中,我们首先通过 bar_index 变量获取当前的K线编号。然后,我们用 strategy.opentrades.entry_bar_index() 函数来获取最后一笔开仓订单的K线编号,并从当前K线编号中减去它。
为了指定我们要获取的是最后一笔订单的数据,我们将其订单编号参数设为 strategy.opentrades - 1。因为PineScript的订单编号是从零开始的索引,所以这个表达式总能定位到最近的那一笔持仓订单。
BarsSinceLastEntry() 函数返回的值有这样的规律:在新订单成交的那根K线上,返回0;在这之后的第一根K线上,返回1;第二根K线上返回2,以此类推。当策略没有任何多头或空头仓位时,也就无所谓距离上次开仓的K线数了,这种情况下 BarsSinceLastEntry() 会返回 na。
在我们的脚本中加入以上函数后,就可以在多处灵活地使用它了。一个直接的用法是将函数返回值存入一个变量,以便后续使用。例如,我们可以用它来绘制距离上一次开仓的K线数:
// 获取距离上次开仓的K线数
barsSinceLast = BarsSinceLastEntry()
// 绘制距离上次开仓的K线数
plot(barsSinceLast, title="距离上次开仓的K线数")
除了直接使用这个信息,我们还可以将其融入到订单条件中。比如说,我们想进行一次额外的多头加仓,但前提是距离上一次开仓已经过去了10根K线。要实现这一点,我们可以检查 BarsSinceLastEntry() 的值是否等于10,同时策略正持有多仓。当条件满足时,再通过 strategy.entry() 发出一个新的开仓订单:
// 在上一次交易10根K线后,生成另一次多头入场
if strategy.position_size > 0 and BarsSinceLastEntry() == 10
strategy.entry("Enter Long", strategy.long)
另一个应用场景是在开仓一定数量的K线后,激活特定的行为。假设我们的策略常在市场剧烈波动时开仓。为了防止止损单在开仓那根K线上被立刻触发,我们可以将止损的提交推迟到开仓后的第二根K线。为此,我们可以检查 BarsSinceLastEntry() 的值,然后通过 strategy.exit() 提交止损单:
// 在上一次开仓2根K线后,提交一个止损单
if BarsSinceLastEntry() >= 1
strategy.exit("Exit Long", from_entry="Enter Long",
stop=math.min(low, low[1]) * 0.9975)
示例策略
让我们看一个完整的策略。下面的脚本在价格跌破近期低点时做空,并在价格高于近期高点时平仓。
我们使用 BarsSinceLastEntry() 函数来实现在距离上一次开仓5根K线后,才对空头仓位进行加仓。这使得策略能够耐心地逐步建仓,而不是在初期就承担过大的风险。该策略的完整代码如下:
//@version=5
strategy(title="Bars since last entry example", overlay=false,
pyramiding=10)
// BarsSinceLastEntry() 函数返回自策略当前持仓中
// 最后一笔、即最近一笔订单入场以来的K线数。
// 当策略没有持仓时,返回 'na'。
BarsSinceLastEntry() =>
bar_index - strategy.opentrades.entry_bar_index(strategy.opentrades - 1)
// 计算近期高点和低点
highestHigh = ta.highest(high, 20)[1]
lowestLow = ta.lowest(low, 20)[1]
// 确定交易条件
initialShortSignal = close < lowestLow and strategy.position_size == 0
pyramidShortSignal = low < lowestLow and BarsSinceLastEntry() > 5
// 根据突破信号生成交易
if initialShortSignal or pyramidShortSignal
strategy.entry("Enter Short", strategy.short)
if close > highestHigh
strategy.close("Enter Short", comment="Exit Short")
// 为验证目的,显示距离上次开仓的K线数
plot(BarsSinceLastEntry(), style=plot.style_columns,
color=color.orange, title="距离上次开仓的K线数")
我们首先用 strategy() 函数配置脚本属性,允许最多10次金字塔式加仓。然后,我们定义了 BarsSinceLastEntry() 函数。
接下来,我们计算了20周期的最高价和最低价。然后,我们使用 BarsSinceLastEntry() 函数来定义交易条件:
// 确定交易条件
initialShortSignal = close < lowestLow and strategy.position_size == 0
pyramidShortSignal = low < lowestLow and BarsSinceLastEntry() > 5
初始的空头信号(initialShortSignal)要求两个条件:一是价格收于近期低点之下,二是策略当前必须是空仓状态(strategy.position_size == 0)。
而金字塔加仓的空头信号(pyramidShortSignal)则相对宽松:只需价格的最低点低于近期低点,并且 BarsSinceLastEntry() 函数的返回值表明距离上一次开仓已经超过了5根K线。
在交易条件之后,策略通过 if 语句来执行开仓和平仓。当 initialShortSignal 或 pyramidShortSignal 满足时,就开立空头仓位。当价格高于近期高点时,则平仓。
策略的最后一部分代码再次使用了 BarsSinceLastEntry() 函数,这次是为了验证:
// 为验证目的,显示距离上次开仓的K线数
plot(BarsSinceLastEntry(), style=plot.style_columns,
color=color.orange, title="距离上次开仓的K线数")
这里 plot() 函数将距离上一次开仓的K线数以橙色的柱状图形式绘制出来。
当我们在图表上运行这个脚本时,我们可以看到它在连续的开仓订单之间,都保持了超过5根K线的间隔。即使有新的低点突破发生,策略也会耐心等待足够的K线数过去:
除了计算距离上一次开仓的K线数,我们也可以测量距离第一次开仓的K线数,那能告诉我们整个仓位已经持有了多久。
简单总结一下:要知道上一次开仓发生在多少根K线之前,我们将当前的K线编号与上一次开仓的K线编号进行比较。我们通过 bar_index 变量获取当前的K线编号,而 strategy.opentrades.entry_bar_index() 函数配合订单编号 strategy.opentrades - 1,可以为我们获取上一次开仓的K线编号。
测量自首次入场以来的K线数
在编写交易策略时,我们有时需要知道当前的持仓已经持有了多少根K线。例如,我们可能想在第一笔交易入场后的第5根K线上进行加仓,或者在入场20根K线后通过时间止损来平仓。在PineScript中我们该如何实现呢?
自定义函数
要计算持仓的持续K线数,我们主要做两件事:首先,获取当前K线的编号;然后,获取当前持仓中第一笔入场订单所在的K线编号。这两者之差就是我们想要的结果。
我们可以将这个逻辑封装成一个自定义函数:
// BarsSinceFirstEntry() 函数返回当前持仓中第一笔入场
// 至今所经过的K线数量。如果策略当前没有持仓,
// 则返回'na'。
BarsSinceFirstEntry() =>
bar_index - strategy.opentrades.entry_bar_index(0)
在这个 BarsSinceFirstEntry() 函数中,我们用当前K线的索引号(bar_index)减去第一笔开仓订单的K线索引号。
我们通过 strategy.opentrades.entry_bar_index() 函数来获取入场K线的索引号。当我们为这个函数传入参数0时,它就会返回当前持仓中第一笔入场交易的K线编号。
这意味着,在开仓的那一根K线上,该函数返回0;在初始入场后的第一根K线上,它返回1;第二根K线上返回2,以此类推。当策略当前没有任何多头或空头仓位时,BarsSinceFirstEntry() 函数会返回 na。
一旦我们将上面的函数加入策略脚本,就可以在多处灵活地使用它。一种用法是先调用该函数,然后将结果保存在一个变量中,以便在脚本的其他地方使用:
// 获取当前持仓自开仓以来经过的K线数
barsSinceEntry = BarsSinceFirstEntry()
// 在图表上绘制这个数值
plot(barsSinceEntry, title="持仓K线数")
我们也可以在条件判断中使用 BarsSinceFirstEntry()。例如,我们想在首次入场后的第10根K线上进行加仓。为此,我们可以检查该函数返回值是否等于10,如果是,就通过 strategy.entry() 函数提交一笔新的交易:
// 在首次入场10根K线后,提交另一笔多头入场(加仓)
if strategy.position_size > 0 and BarsSinceFirstEntry() == 10
strategy.entry("Enter Long", strategy.long)
策略还可以从某个K线数之后开始执行某个动作。例如,我们想在入场几根K线后才开始设置止损。要实现这一点,我们可以检查 BarsSinceFirstEntry() 的值,然后再提交止损单:
// 在初始入场4根K线后,提交止损单
if BarsSinceFirstEntry() > 3
strategy.exit("Exit Long", from_entry="Enter Long", stop=low[1] * 0.995)
示例策略
让我们通过一个完整的策略来探讨。下面的脚本基于EMA均线交叉进行多头交易。我们使用 BarsSinceFirstEntry() 函数在首次入场5根K线后进行一次加仓。并且,当持仓达到20根K线时,我们就将其平仓。
策略的代码如下:
//@version=5
strategy(title="持仓K线数示例", pyramiding=2)
// BarsSinceFirstEntry() 函数返回当前持仓中第一笔入场
// 至今所经过的K线数量。如果策略当前没有持仓,
// 则返回'na'。
BarsSinceFirstEntry() =>
bar_index - strategy.opentrades.entry_bar_index(0)
// 计算EMA
fastEMA = ta.ema(close, 10)
slowEMA = ta.ema(close, 30)
// 生成交易
if ta.crossover(fastEMA, slowEMA) or
(strategy.position_size > 0 and BarsSinceFirstEntry() == 5)
strategy.entry("Enter Long", strategy.long)
if BarsSinceFirstEntry() == 20
strategy.close("Enter Long", comment="时间止损平仓")
// 为了验证,在图表上绘制自首次入场以来的K线数
plot(BarsSinceFirstEntry(), style=plot.style_columns,
color=color.teal, title="持仓K线数")
我们首先用 strategy() 函数配置策略,设置 pyramiding=2 允许在同方向最多进行2次开仓。然后,我们包含了前面讨论的 BarsSinceFirstEntry() 辅助函数。
接下来,代码计算了两条EMA均线,并设置了 if 语句来生成交易:
if ta.crossover(fastEMA, slowEMA) or
(strategy.position_size > 0 and BarsSinceFirstEntry() == 5)
strategy.entry("Enter Long", strategy.long)
这个 if 语句包含了两个通过 or 连接的条件:一是快线(fastEMA)上穿了慢线(slowEMA);二是策略已持有多仓(strategy.position_size > 0)并且持仓刚好达到5根K线(BarsSinceFirstEntry() == 5)。当这两个条件中的任意一个为真时,策略就会通过 strategy.entry() 开立一个多头仓位。
接着,我们使用 BarsSinceFirstEntry() 来实现时间止损:
if BarsSinceFirstEntry() == 20
strategy.close("Enter Long", comment="时间止损平仓")
当持仓时间确实达到了20根K线时,strategy.close() 函数就会平掉这个多头仓位。
最后,我们还将持仓K线数绘制在图表上以便观察:
// 为了验证,在图表上绘制自首次入场以来的K线数
plot(BarsSinceFirstEntry(), style=plot.style_columns,
color=color.teal, title="持仓K线数")
这段代码会以柱状图的形式,在副图上显示 BarsSinceFirstEntry() 函数的当前值。
在图表上,该策略会计算自初始入场以来的K线数。当达到5根K线时,会进行一次加仓。当持仓达到20根K线时,策略就会平仓离场:
简单总结一下:要计算策略持仓的持续K线数,我们用当前K线的编号减去第一笔入场订单的K线编号。我们通过 bar_index 变量获取当前K线的编号,通过 strategy.opentrades.entry_bar_index(0) 函数(参数为0)来获取第一笔入场订单的K线编号。




