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

Pine Script(211):平仓的三种方式 - 市价、止盈与止损

#Pine Script入门教学

用市价单平仓

平掉策略的虚拟仓位,一个最常见的方法就是使用市价单。在Pine Script中,有两个函数可以生成这样的平仓指令:

  • strategy.close() 函数:平掉所有具有特定名称的开仓交易。
  • strategy.close_all() 函数:简单粗暴地平掉所有开仓交易。

接下来,让我们看看这两种方法的具体用法。

平掉多头交易

要平掉一个特定的多头交易,我们使用 strategy.close() 函数。要做到这一点,我们需要为该函数提供一个必需的参数:我们想要平仓的、已开仓多头订单的订单ID。

例如:

strategy.close("Enter Long")

这行代码会指示 strategy.close() 函数通过一个市价单,来平掉之前开仓时ID为 "Enter Long" 的那笔交易。

但上面的代码片段缺少了一个关键环节:何时发送这个平仓订单。按照现在的写法,它会在每根K线上都尝试平掉名为 "Enter Long" 的订单,这几乎会导致交易在开仓后被立刻平掉。

正确的做法是,将 strategy.close() 放置在一个 if 语句中。这样,该函数就只会在 if 的条件判断为 true 时才被执行,从而实现在特定情况下才进行平仓。

代码如下:

// 当价格下穿加权移动平均线(WMA)时,
// 提交一个市价单来平掉ID为 'Enter Long' 的订单
if ta.crossunder(close, ta.wma(close, 15))
	strategy.close("Enter Long")

这个 if 语句使用 ta.crossunder() 函数来检查当前K线的收盘价(close)是否下穿了15周期的加权移动平均线。只有当这个条件成立时,if 语句内部的代码才会被执行。

if 语句内部,strategy.close() 函数会平掉所有ID为 "Enter Long" 的开仓订单。这样一来,市价单平仓就只会在满足我们设定的特定条件时发生。

平掉空头交易

我们平掉空头交易的方式与平掉多头交易完全相同。我们调用 strategy.close() 函数,并至少提供一个参数:我们想要平仓的、已开仓空头订单的订单ID。

如果策略的空头入场订单ID是 “Enter Short”,那么 strategy.close() 会像这样通过市价单平掉该笔交易:

strategy.close("Enter Short")

为了避免在每根K线上都尝试平仓,我们需要一个条件来控制平仓的时机。因此,我们同样将 strategy.close() 放入一个 if 语句中:

// 当价格上穿加权移动平均线(WMA)时,
// 提交一个市价单来平掉ID为 'Enter Short' 的订单
if ta.crossover(close, ta.wma(close, 15))
	strategy.close("Enter Short")

这个 if 语句使用 ta.crossover() 函数来判断收盘价是否上穿了15周期的WMA。如果条件成立,strategy.close() 函数就会被调用,以市价单平掉所有ID为 “Enter Short” 的空头交易。

平掉所有仓位

另一种市价单平仓的方式是使用 strategy.close_all() 函数。这个函数会通过一笔市价单,平掉所有已开仓的订单,一步到位。

无论策略当前是持有多头还是空头仓位,strategy.close_all() 都会自动选择正确的市价单方向(卖出或买入)来平掉所有交易。

strategy.close_all() 在以下场景中特别有用:

  • 当策略可能同时持有多笔不同ID的订单时。与其为每个ID都调用一次 strategy.close(),不如用一次 strategy.close_all() 来全部清仓。
  • 在某些特定时刻需要强制清仓时,无论持的是多单还是空单。(例如,在周末收盘前,或在回测周期结束时清空所有仓位。)

由于 strategy.close_all() 没有任何必需的参数,我们只需直接调用它即可:

strategy.close_all()

当然,我们通常不希望在每根K线上都执行清仓操作。所以,我们应该将 strategy.close_all() 放入一个 if 语句中,以设定特定的触发条件:

// 当价格穿越200周期EMA时,
// 提交一个市价单来平掉策略当前的所有仓位
if ta.cross(close, ta.ema(close, 200))
	strategy.close_all()

这个 if 语句使用 ta.cross() 函数来判断收盘价是否与200周期的EMA发生了交叉(无论上穿还是下穿)。当交叉发生时,if 语句块内的 strategy.close_all() 函数就会被执行,以市价单平掉策略的所有仓位。

示例策略

让我们通过一个完整的策略来看看如何使用市价单平仓。下面的脚本基于肯特纳通道(Keltner Channel)进行交易。当价格上穿上轨时做多,跌破下轨时做空。

策略有三种市价单平仓逻辑:当持有多仓时,如果价格再次上穿上轨,则平掉多头仓位。类似地,当持有空仓时,如果价格再次下穿下轨,则平掉空头仓位。此外,如果价格穿越了肯特纳通道的中线,我们也会清空所有仓位。

策略的完整代码如下:

//@version=5
strategy(title="市价单平仓", overlay=true)

// 计算并绘制肯特纳通道
[keltMa, keltUpper, keltLower] = ta.kc(close, 20, 1.75)
plot(keltUpper, color=color.blue, title="上轨")
plot(keltMa, color=color.orange, title="肯特纳中线")
plot(keltLower, color=color.blue, title="下轨")

// --- 入场逻辑 ---
// 当价格上穿肯特纳上轨时做多
if ta.crossover(close, keltUpper)
    strategy.entry("Enter Long", strategy.long)

// 当价格下穿肯特纳下轨时做空
if ta.crossunder(close, keltLower)
    strategy.entry("Enter Short", strategy.short)

// --- 平仓逻辑 ---
// 当(持有多仓时)价格再次上穿肯特纳上轨,用市价单平掉多头交易
if ta.crossover(close, keltUpper)
    strategy.close("Enter Long", comment="多头平仓")

// 如果(持有空仓时)价格再次下穿肯特t纳下轨,用市价单平掉空头交易
if ta.crossunder(close, keltLower)
    strategy.close("Enter Short", comment="空头平仓")

// 当价格穿越中线时,用市价单清空策略所有仓位
if ta.cross(close, keltMa)
    strategy.close_all(comment="清空所有仓位")

我们首先计算并绘制了肯特纳通道。

入场逻辑很简单:价格上穿上轨时,通过 strategy.entry("Enter Long", strategy.long) 做多;价格下穿下轨时,通过 strategy.entry("Enter Short", strategy.short) 做空。

平仓逻辑包含三个部分:

  1. 平多单:if ta.crossover(close, keltUpper) 条件在这里被复用。如果策略已持有多仓,当价格再次出现上穿上轨的信号时,我们将其视为一个平仓信号。strategy.close("Enter Long", comment="多头平仓") 会平掉ID为 "Enter Long" 的多头订单。comment 参数可以为这笔平仓交易在图表和交易报告中提供一个清晰的注释。
  2. 平空单:逻辑与平多单类似。如果策略已持有空仓,当价格再次下穿下轨时,strategy.close("Enter Short", comment="空头平仓") 会平掉空头订单。
  3. 清空所有仓位:if ta.cross(close, keltMa) 判断价格是否与肯特纳通道的中线发生交叉。这是一个中性的信号,我们用它来清空所有持仓。strategy.close_all(comment="清空所有仓位") 会不论当前持的是多单还是空单,都将其全部平掉。

在图表上,该策略会根据肯特纳通道的信号,通过市价单进行交易,如下所示:

总结

  • TradingView策略主要通过两种方式使用市价单平仓。
  • strategy.close() 函数平掉所有具有特定ID的订单。
  • strategy.close_all() 函数则以市价单平掉策略的所有仓位(无论多空或ID)。
  • 我们必须将这些平仓函数放置在 if 语句中,以确保它们只在满足特定条件时才执行。
  • 重要提示:这两个函数只负责平仓,它们不能在平仓的同时反向开仓。

用止盈限价单退出交易

TradingView策略使用 strategy.exit() 函数来生成止盈订单(Profit Target)。通过这种方式,策略可以使用一个限价单来平掉其模拟仓位。

strategy.exit() 函数有多种配置方式,但要创建一个止盈订单,至少需要以下三个核心参数:

  1. 订单ID:为平仓订单设置一个名称,这是第一个必需的参数。例如,我们可以用 "Exit Long""Exit Short" 来分别命名多头和空头的止盈订单。
  2. from_entry 参数:指定这个平仓订单是针对哪一个开仓订单的。通过这个参数,Pine Script就能将平仓与特定的开仓建立关联。
  3. 止盈目标:配置止盈的具体价位。我们有两种方式来实现:
    • 使用 profit 参数:设置一个基于点数(ticks)的盈利目标,即从开仓价算起盈利多少个点。
    • 使用 limit 参数:设置一个具体的价格作为止盈目标。

下面,我们来运用这三个步骤,编写多头和空头的止盈平仓逻辑。

多头平仓

根据上述步骤,我们有两种设置多头止盈目标的方法:按点数或按价格。

要通过盈利点数来设置多头平仓位,我们可以像这样使用 strategy.exit()

strategy.exit("Exit Long", from_entry="Enter Long", profit=200)

在这里,strategy.exit() 创建了一个名为 “Exit Long” 的平仓订单。它专门用于平掉名为 "Enter Long" 的开仓订单。profit 参数将止盈目标设在距离开仓价格200个点的位置。

要在一个指定的价格平仓,我们可以这样使用 strategy.exit()

strategy.exit("Exit Long", from_entry="Enter Long", limit=close * 1.05)

这个代码片段同样创建了一个名为 “Exit Long” 的平仓订单,用于平掉 “Enter Long” 的仓位。但这一次,limit 参数将止盈目标设为了当前K线收盘价上涨5%后的价位。

然而,以上两个示例都忽略了一个关键问题:应该在何时发送这个平仓订单?按照当前写法,策略会在每一根K线上都生成平仓订单。这虽然不会导致Pine Script报错,但对于基于价格的止盈目标来说,它会导致目标价位在每根K线上都发生变化,这通常不是我们想要的结果。

那么,发送止盈订单的最佳时机是什么时候呢?一个理想的时刻就是在生成开仓订单的同时。这样,一旦开仓成交,止盈订单就能立刻生效。

在代码实现上,这意味着当 strategy.entry()strategy.order() 执行开仓操作时,我们也应该立即执行 strategy.exit()

因此,我们的策略代码应该这样写:

// 当价格上穿20周期均线时,执行做多,
// 同时生成一个距离开仓价200点的止盈订单
if ta.crossover(close, ta.sma(close, 20))
	strategy.entry("Enter Long", strategy.long)
	strategy.exit("Exit Long", from_entry="Enter Long", profit=200)

这个 if 语句通过 ta.crossover() 函数判断收盘价是否上穿了20周期简单移动平均线。一旦条件满足,if 语句块内的代码会执行两项操作:

首先,strategy.entry() 函数生成一个市价单来开立一个名为 "Enter Long" 的多头仓位(strategy.long)。

紧接着,在同一时刻,strategy.exit() 函数生成一个名为 "Exit Long" 的平仓订单。from_entry 参数指明了这个平仓订单是为我们刚刚创建的 “Enter Long” 订单服务的。profit 参数则将止盈目标设在开仓价上方200个点。

当然,我们也可以使用具体价格来设置止盈目标,只需将 strategy.exit()profit 参数替换为 limit 参数即可。

注意:我们无需在其他订单平仓后手动取消由 strategy.exit() 创建的止盈单。因为 strategy.exit() 已经通过 from_entry 参数与开仓订单绑定。一旦那个开仓订单被平掉,TradingView会自动移除与之关联的止盈订单。

空头平仓

与多头止盈类似,空头止盈也有两种选择:基于点数的或基于价格的平仓。

要通过盈利点数来平掉空头仓位,我们可以这样使用 strategy.exit()

strategy.exit("Exit Short", from_entry="Enter Short", profit=175)

在这里,strategy.exit() 创建了一个名为 “Exit Short” 的平仓订单,用于平掉 "Enter Short" 的仓位。profit 参数将止盈目标设在开仓价下方175个点。

要在一个指定价格平掉空头仓位,可以像这样使用 strategy.exit()

strategy.exit("Exit Short", from_entry="Enter Short", limit=close * 0.97)

现在,strategy.exit() 通过 limit 参数将止盈目标设为当前K线收盘价下跌3%后的价位。

同样地,这两个代码片段都会在每根K线上持续发送平仓订单。为了避免这个问题,最佳实践是在创建空头开仓订单的同时就生成止盈订单。

示例如下:

// 当价格下穿20周期均线时,生成一个空头开仓单,
// 并为其设置一个175点的止盈订单
if ta.crossunder(close, ta.sma(close, 20))
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", profit=175)

这个 if 语句检查价格是否下穿了简单移动平均线。如果条件满足,代码块会执行两个函数:

首先,strategy.entry() 生成一个名为 “Enter Short” 的市价单来开立空头仓位(strategy.short)。

同时,strategy.exit() 生成一个名为 "Exit Short" 的平仓订单,通过 from_entry 与刚刚的开仓单绑定,并设置一个175点的盈利目标。

完整策略示例

让我们看一个使用止盈目标进行平仓的完整策略。下面的脚本基于相对强弱指数(RSI)进行交易。

当RSI上穿20时,策略以市价单做多,并提交一个设在当前K线最高价上方3%的止盈目标。同样,当RSI下穿80时,策略以市价单做空,并为其设置一个在当前K线最低价下方3%的止盈目标。

策略的完整代码如下:

//@version=5
strategy(title="Limit order exits")

// 计算相对强弱指数 (RSI)
rsiValue = ta.rsi(hlcc4, 4)

// 绘制RSI及其超买超卖线
plot(rsiValue, color=color.teal, title="RSI")
hline(20, title="Oversold")
hline(80, title="Overbought")

// 当RSI上穿20时做多,并为该仓位
// 生成一个高于最高价3%的止盈订单
if ta.crossover(rsiValue, 20)
    strategy.entry("Enter Long", strategy.long)
    strategy.exit("Exit Long", from_entry="Enter Long", 
         limit=high * 1.03)

// 当RSI下穿80时做空(并提交一个3%的止盈目标)
if ta.crossunder(rsiValue, 80)
    strategy.entry("Enter Short", strategy.short)
    strategy.exit("Exit Short", from_entry="Enter Short",
         limit=low * 0.97)

我们首先用 strategy() 函数命名脚本。然后用 ta.rsi() 计算4周期的RSI,并用 plot()hline() 将其及超买(80)超卖(20)水平线绘制出来。

接着是多头入场和止盈的逻辑:

// 当RSI上穿20时做多,并为其生成止盈订单
if ta.crossover(rsiValue, 20)
    strategy.entry("Enter Long", strategy.long)
    strategy.exit("Exit Long", from_entry="Enter Long", 
         limit=high * 1.03)

ta.crossover() 检测到RSI上穿20时,if 语句内的代码会执行。首先 strategy.entry() 开立一个名为 "Enter Long" 的多头市价单。紧接着,strategy.exit() 为这个刚开的仓位创建一个名为 "Exit Long" 的止盈订单,from_entry 将两者绑定,limit 则将止盈价格设在当时K线最高价的1.03倍。

然后是空头入场和止盈的逻辑:

// 当RSI下穿80时做空(并提交3%的止盈目标)
if ta.crossunder(rsiValue, 80)
    strategy.entry("Enter Short", strategy.short)
    strategy.exit("Exit Short", from_entry="Enter Short",
         limit=low * 0.97)

ta.crossunder() 检测到RSI下穿80时,strategy.entry() 开立一个名为 "Enter Short" 的空头市价单。同时,strategy.exit() 为其创建一个名为 "Exit Short" 的止盈单,止盈价格设在当时K线最低价的0.97倍。

在图表上,该策略使用止盈订单平仓的效果如下:

总结

  • TradingView策略使用 strategy.exit() 函数来创建止盈订单。
  • 创建此类订单需要三个核心参数:订单ID、要平仓的开仓订单名称(from_entry),以及止盈价位。
  • 设定止盈价位有两种方式:使用 profit 参数指定盈利的点数,或使用 limit 参数指定一个具体的价格。
  • 最佳实践是在提交开仓交易的同时就生成止盈订单。这样,一旦开仓成交,止盈单便能立刻生效,从而实现精准的即时风险管理。

用止损单退出交易

在TradingView策略中,我们使用 strategy.exit() 函数来生成止损单(stop-loss orders)。通过这种方式,策略可以在亏损达到特定数额时,以市价单平掉其虚拟仓位。

我们可以通过多种方式使用 strategy.exit() 函数。要通过止损单平仓,需要提供以下几个关键参数:

  • 设置订单ID:通过第一个参数设置平仓订单的唯一名称。例如,我们可以用 "Exit Long""Exit Short" 来分别命名多头和空头的平仓单。
  • 使用 from_entry 参数:这个参数用来指定该平仓指令是针对哪一个入场订单的。通过这种方式,Pine Script就能将止损单与特定的入场交易关联起来。
  • 指定止损水平。这里有两种设置方式:
    • loss:设置一个从开仓价格算起的价差(以tick为单位)作为止损距离。
    • stop:直接设置一个具体的价位作为止损价格。

重要提示:我们不需要使用 strategy.cancel()strategy.cancel_all() 来手动取消这个止损单。当我们使用 from_entry 将一个止损单附加到特定的入场交易上时,TradingView会自动管理。如果该笔交易被其他订单(如止盈单)平仓了,那么这个尚未触发的止损单会被系统自动撤销。

接下来,让我们通过代码来演示如何为多头和空头仓位设置止损。

多头止损平仓

如上所述,设置多头仓位的保护性止损有两种方法:基于价差(ticks)或基于具体价格。

第一种方法,基于价差的止损,使用 strategy.exit() 函数的代码如下:

// 为ID为 "Enter Long" 的入场单,生成一个亏损150个最小变动单位(ticks)的止损单
strategy.exit("Exit Long", from_entry="Enter Long", loss=150)

这里的 strategy.exit() 创建了一个名为 "Exit Long" 的平仓订单。我们通过 from_entry="Enter Long" 告诉它这个止损是专门用于平掉ID为 "Enter Long" 的那笔入场交易的。loss=150 则将止损位设置在开仓价下方150个最小变动单位(ticks)的位置。(TradingView会自动根据这个点数计算出具体的止损价格。)

第二种方法,基于具体价格的止损,代码如下:

// 为ID为 "Enter Long" 的入场单,提交一个止损单,其价格在当前K线最低价的下方2%
strategy.exit("Exit Long", from_entry="Enter Long", stop=low * 0.98)

这段代码同样创建了一个名为 "Exit Long" 的平仓单,并关联到 "Enter Long" 的入场。但这次,我们使用 stop 参数,直接将止损价格设定为当前K线最低价的98%。

上面的两个示例代码都缺少一个关键环节:何时发送这个止损单。按照现在的写法,它们会在每根K线上都执行,这不是最佳实践。

最好的做法是,在生成入场订单的同时生成止损单。这样做有两个核心优势:

  1. 即时保护:一旦入场单成交,止损单会立刻被激活,从而确保仓位从一开始就受到保护。如果等到持仓后再挂止损,会存在至少一根K线的延迟,这期间仓位是无保护的。
  2. 避免止损跟着价格跑:如果我们在每根K线上都更新止损价格(例如,始终设在当前K线最低价下方8%),那么随着价格的波动,止损位会一直跟着移动,可能永远不会被触发。因此,我们应该在入场时就确定好止损位,而不是让它一直漂移。

将入场和止损同时生成的代码如下:

// 当价格上穿过去20根K线的最高点时,做多入场,
// 同时设置一个距离开仓价150个最小变动单位的止损
if ta.crossover(high, ta.highest(high, 20)[1])
	strategy.entry("Enter Long", strategy.long)
	strategy.exit("Exit Long", from_entry="Enter Long", loss=150)

这个 if 语句判断价格是否突破了过去20根K线的最高点。当条件满足时,代码块内的指令被执行。首先,strategy.entry() 提交一个名为 "Enter Long" 的多头市价单。接着,strategy.exit() 提交一个名为 "Exit Long" 的平仓单,通过 from_entry 将它与 "Enter Long" 关联,并通过 loss 设置止损距离。

在后台,TradingView的运行机制很智能:它会先提交入场单,等待其成交后,再立刻激活与之关联的止损单。因此,我们完全可以在代码中将入场和止损指令写在一起,不必担心时序问题。

空头止损平仓

与多头止损类似,为空头仓位设置止损也有两种方式:基于价差(ticks)或基于具体价格。

基于价差的止损:

// 为ID为 "Enter Short" 的入场单,生成一个亏损235个最小变动单位的止损单
strategy.exit("Exit Short", from_entry="Enter Short", loss=235)

基于具体价格的止损:

// 为ID为 "Enter Short" 的入场单,提交一个止损单,其价格在当前K线最高价的上方2%
strategy.exit("Exit Short", from_entry="Enter Short", stop=high * 1.02)

与多头一样,最佳实践是在生成空头入场订单的同时生成其止损单。原因同样是实现即时保护,并防止止损价格随行情漂移。

代码示例如下:

// 当价格下穿过去20根K线的最低点时,做空入场,
// 同时设置一个距离开仓价235个最小变动单位的止损
if ta.crossunder(low, ta.lowest(low, 20)[1])
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", loss=235)

if 条件满足时,代码先通过 strategy.entry() 提交一个名为 "Enter Short" 的空头市价单。紧接着,strategy.exit() 提交一个与之关联的止损单。TradingView的后台机制会处理好这一切,确保在入场成交后止损才被激活。

示例策略

让我们通过一个完整的策略来看看如何使用 strategy.exit() 生成止损单。下面的脚本基于两条指数移动平均线(EMA)的交叉进行交易。

当快线上穿慢线时,策略做多;当快线下穿慢线时,策略做空。两种入场都会通过 strategy.exit() 设置一个保护性止损。止损价格设定在慢速均线附近,距离为10个最小变动单位(ticks)。

策略的完整代码如下:

//@version=5
strategy(title="止损单平仓", overlay=true)

// 计算并绘制指数移动平均线 (EMA)
fastEMA = ta.ema(close, 10)
slowEMA = ta.ema(close, 30)
plot(fastEMA, color=color.orange, title="快线EMA")
plot(slowEMA, color=color.teal, linewidth=2, title="慢线EMA")

// 当快线上穿慢线时做多
if ta.crossover(fastEMA, slowEMA)
    strategy.entry("Enter Long", strategy.long)
    strategy.exit("Exit Long", from_entry="Enter Long", 
         stop=slowEMA - syminfo.mintick * 10)

// 当快线下穿慢线时做空
if ta.crossunder(fastEMA, slowEMA)
    strategy.entry("Enter Short", strategy.short)
    strategy.exit("Exit Short", from_entry="Enter Short", 
         stop=slowEMA + syminfo.mintick * 10)

代码分三个部分:

  1. 计算指标:我们先计算并绘制了10周期和30周期的EMA。
  2. 多头逻辑:当 ta.crossover(fastEMA, slowEMA) 条件满足时,if 语句块被执行。内部代码首先调用 strategy.entry() 做多,然后立刻调用 strategy.exit() 设置止损。止损单名为 "Exit Long",通过 from_entry 关联到 "Enter Long" 入场单,并通过 stop 将止损价格设在慢速EMA下方10个最小变动单位的位置。
  3. 空头逻辑:当 ta.crossunder(fastEMA, slowEMA) 条件满足时,逻辑与多头类似。先调用 strategy.entry() 做空,然后立刻调用 strategy.exit() 设置止损,止损价格设在慢速EMA上方10个最小变动单位的位置。

在图表上,该策略会根据均线交叉进行交易,并为每笔交易设置保护性止损,如下所示:

总结

  • TradingView策略使用 strategy.exit() 函数来创建止损单。
  • 要创建这样一个订单,该函数需要三个关键参数:订单ID、通过 from_entry 关联的入场订单ID,以及止损水平。
  • 止损水平可以通过 loss(基于价差)或 stop(基于具体价格)来设定。
  • 最佳实践是在提交入场订单的同时提交止损单。这样,当入场单成交后,TradingView会立即激活止损,为交易提供即时保护。
赞(0)
未经允许不得转载:图道交易 » Pine Script(211):平仓的三种方式 - 市价、止盈与止损
分享到

评论 抢沙发

登录

找回密码

注册