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

Pine Script(216):strategy.exit()与strategy.order()

#Pine Script入门教学

用strategy.exit()关闭多头和空头交易

一个PineScript策略通过在TradingView图表上模拟交易来评估一个交易思路的表现。为此,我们需要开仓和平仓。strategy.exit() 函数就是用于让策略平掉多头和空头仓位的函数。

通过 strategy.exit(),我们的代码可以生成三种类型的平仓订单:止损单、止盈单(限价单)和追踪止损单。我们先来看看该函数的默认语法结构,然后再通过代码示例来探讨如何使用它。

默认语法结构

strategy.exit() 函数的完整语法结构包含以下参数:

strategy.exit(id, from_entry, qty, qty_percent, profit, limit, 
	 loss, stop, trail_price, trail_points, trail_offset, 
	 oca_name, comment, comment_profit, comment_loss, comment_trailing, 
	 when, alert_message, alert_profit, alert_loss, alert_trailing)

参数非常多!但幸运的是,我们通常不需要同时使用所有这些参数。为了让信息更易于理解,我们将这些参数分组进行讨论。首先是标准参数,然后是订单参数,接着是注释参数,最后是警报参数。

标准参数

strategy.exit() 包含以下几个通用的标准参数:

  • id:一个必需的字符串,用于设定平仓订单的唯一ID(即订单名称)。每个平仓订单都必须有一个名称,以便后续可以通过这个ID来修改或取消它。
  • from_entry:用于指定 strategy.exit() 是针对哪一个入场订单进行平仓。我们需要提供那个入场订单的ID字符串。这个参数实现了两个重要功能。一是实现非先进先出的平仓顺序:默认情况下,策略是按先进先出的原则平仓,但如果我们将策略的 close_entries_rule 属性设为 "ANY",就可以通过 from_entry 来指定平掉某一个特定的入场单。二是为尚未成交的入场单预设平仓:这个功能非常强大,它允许我们为即将成交的入场单(即挂单)预先设置好平仓指令,这样一旦入场单成交,与之关联的平仓单就会被立刻激活。如果不使用 from_entry,那么 strategy.exit() 指令会应用于当前所有持仓(这是其默认行为)。
  • qty:一个数字,用于指定要平仓的合约、股票或单位的数量。如果不设置此参数或将其设为 nastrategy.exit() 将平掉其所关联的入场单的全部数量(100%)。如果 qty 小于当前持仓量,会执行部分平仓。如果策略持有多个入场单,且未使用 from_entry 指定特定目标,那么 strategy.exit() 会从每个入场单中都平掉 qty 指定的数量。例如,如果有3个开仓订单,qty=2 将总共平掉6手(2手×3个订单)。如果 qty 大于某个入场单的持仓量,strategy.exit() 只会平掉该订单的全部数量,它永远不会反向开仓或从其他订单中借调数量来满足 qty 的设定。如果一次 strategy.exit() 调用同时提交了止损和止盈单,那么设定的数量将同时应用于这两个订单,若想为它们设置不同数量,需要分两次调用。特别要注意:qty 设定的固定数量在策略加仓后不会自动更新。例如,你为一个5手的仓位设了止盈,之后又加仓了10手,当止盈触发时,它仍然只会平掉最初设定的5手。要解决这个问题,你需要更新这个平仓订单。
  • qty_percent:用一个0.0到100.0之间的数字来指定平仓数量的百分比。此参数的优先级高于 qty,如果同时设置了两者,系统会以 qty_percent 为准。如果设为 na,则默认平掉全部仓位(100%),除非 qty 已设定了数量。有一点很重要:平仓百分比是基于当前的持仓量计算的。例如,你为10手多仓设置了50%的止盈,之后又加仓了10手(总共20手),当止盈触发时,系统会平掉10手(20手的50%),而不是最初的5手。

订单参数

以下这些参数用于配置具体的订单类型和价格:

  • profit:基于价差(ticks)来设置止盈目标。系统会根据开仓均价和这个价差来计算出具体的限价单价格。如果 profit 设为0或负数,会导致立即平仓。profit 的计算不包含手续费。
  • limit:直接指定止盈订单的具体价格。这是一个限价单,会以该价格或更优价格成交。profitlimit 都用于生成止盈限价单,但两者只能选其一,如果同时设置,系统会优先使用 limit
  • loss:基于价差(ticks)来设置止损。系统会根据开仓均价和这个价差来计算出具体的止损单价格。如果 loss 设为0或负数,会导致立即平仓。loss 的计算也不包含手续费。
  • stop:直接指定止损订单的具体价格。这是一个停止单,会以该价格或更差价格成交。lossstop 都用于生成止损单,两者也只能选其一,如果同时设置,系统会优先使用 stop
  • trail_price:指定追踪止损开始激活的具体价格。一旦市场价格触及此价位,追踪止损便会启动,之后它会以 trail_offset 指定的距离跟随最优价格。使用 trail_price 时,必须同时设置 trail_offset,否则会报错。
  • trail_points:指定在盈利达到多少个最小变动单位(ticks)后激活追踪止损。启动后,它同样以 trail_offset 指定的距离跟随最优价格,也必须同时设置 trail_offset。如果 trail_points 设为0,追踪止损会在入场成交后立即激活。注意,虽然参数名叫points,但它指的是ticks。一旦追踪止损被激活,它就会持续生效,即使后续浮动盈利回落到激活点以下,它也不会失效。我们可以同时设置 trail_pointstrail_price,哪个条件先被满足,哪个就负责激活追踪止损。
  • trail_offset:设置追踪止损跟随最优价格的距离(以ticks为单位)。对于多头,止损位会设在追踪激活后所达到的最高价下方 trail_offset 的位置;对于空头,止损位会设在追踪激活后所达到的最低价上方 trail_offset 的位置。trail_offset 只有在追踪止损被 trail_pricetrail_points 激活后才起作用。
  • oca_name:用一个字符串来指定 strategy.exit() 所生成的订单所属的订单组名称。通过 oca_name,我们可以将多个平仓订单(例如一个止盈单和一个止损单)归入一个自定义的组。当组内任何一个订单成交时,PineScript会自动减少同组其他订单的数量(减少的数量等于已成交订单的数量)。如果不设置 oca_name,每次调用 strategy.exit() 都会生成一个独立的订单组,这意味着当一个平仓单成交后,其他挂起的平仓单(即使是同一次调用生成的)不会受到影响。strategy.exit() 内部使用的订单组类型是ORO(One-Reduces-Other,一单成交他单减量),这是固定行为,无法更改。当一次调用同时生成了止盈和止损单时,这两个订单会自动被放入同一个订单组。
  • when:一个布尔值,用于设定平仓订单的触发条件。如果为 true,PineScript才会生成 strategy.exit() 订单。该参数的默认值为 true,这意味着如果不设置 when,只要代码执行到 strategy.exit(),订单就会被提交。这使得我们可以将其方便地放入 if 语句中,通过 if 的条件来控制订单的生成时机。

注释参数

strategy.exit() 函数还提供了一系列参数,用于配置订单的注释。这些文本注释会显示在图表上订单成交的箭头标记附近,同时也会出现在策略测试器窗口的交易列表标签页中。我们可以利用注释来为订单、策略或交易品种提供额外的备注和信息。

可用的注释参数如下:

  • comment:设置一个通用的订单注释字符串。除非我们使用了下述更专用的注释参数,否则这个文本会应用于所有通过 strategy.exit() 成交的订单(止盈、止损和追踪止损)。
  • comment_profit:专门为止盈单(由 profitlimit 参数生成)设置注释。这个参数会覆盖通用的 comment 参数,因此对于止盈单,最终只会显示一个注释。
  • comment_loss:专门为止损单(由 lossstop 参数生成)设置注释。同样会覆盖通用的 comment 参数。
  • comment_trailing:专门为追踪止损单(由 trail_offset 等参数生成)设置注释。也会覆盖通用的 comment 参数。

警报参数

strategy.exit() 的最后一组参数用于配置订单的警报消息。它的工作原理如下:当我们在创建警报窗口中为策略启用订单成交警报时,每次有订单成交,策略就会触发一个警报。默认情况下,警报内容是我们手动输入的文本。

但是,如果我们在警报文本中使用了 {{strategy.order.alert_message}} 这个占位符,那么策略就会用我们在订单函数中通过代码设定的文本来替换这个占位符。这样,strategy.exit() 就能在它的任何一个订单成交时,生成一个完全自定义的警报。

用于设定占位符替代文本的参数如下:

  • alert_message:定义一个通用的警报消息字符串。除非使用了下述更专用的警报参数,否则这个文本会应用于所有成交的 strategy.exit() 订单。
  • alert_profit:专门为止盈单设置警报文本,会覆盖通用的 alert_message 参数。
  • alert_loss:专门为止损单设置警报文本,会覆盖通用的 alert_message 参数。
  • alert_trailing:专门为追踪止损单设置警报文本,同样会覆盖通用的 alert_message 参数。

所需的最少参数

strategy.exit() 的参数虽然多,但哪些是绝对必要的呢?幸运的是,并不多。要发出一个最基本的平仓指令,我们总是先使用 id 参数设置订单的唯一ID(名称),然后决定要生成哪种平仓订单:止盈单用 profit(基于价差)或 limit(基于价格);固定止损单用 loss(基于价差)或 stop(基于价格);追踪止损单则组合使用 trail_offsettrail_pricetrail_points

这就是最基本的要求。现在,让我们来编写几个 strategy.exit() 的示例代码。

三种订单类型

strategy.exit() 可以生成的订单类型有:限价单(即止盈单)、停止单(即止损单)和追踪止损单。具体生成哪种订单,取决于我们如何使用 profitlimitlossstoptrail_pricetrail_pointstrail_offset 这些参数。

下表总结了如何通过不同参数组合来创建各种订单:

平仓订单类型 如何通过 strategy.exit() 创建
止盈单(Limit) 要设置一个基于价差的止盈,需设定 profit 参数。要设置一个基于具体价格的止盈,需设定 limit 参数。
止损单(Stop) 要设置一个基于价差的止损,需设定 loss 参数。要设置一个基于具体价格的止损,需设定 stop 参数。
追踪止损(Trailing Stop) 首先,通过 trail_points(盈利价差)或 trail_price(具体价格)设定激活条件。然后,通过 trail_offset 参数设定追踪距离。

提示:一次 strategy.exit() 函数调用可以同时发送两个订单:一个止盈单和一个止损单(固定止损或追踪止损)。这是一种为交易同时设置止盈和止损的便捷方式。如果一次调用同时配置了固定止损和追踪止损,PineScript只会生成那个离当前市场价格更近的止损单。

现在,让我们为每种可能的 strategy.exit() 订单类型编写代码。

限价单(止盈单)

要使用 strategy.exit() 生成一个止盈单,我们主要做两件事:通过函数的第一个参数设置订单ID,然后决定如何指定止盈价格——基于价格用 limit 参数设置一个具体的平仓价格,基于价差则用 profit 参数设置一个从开仓均价算起的盈利点数(ticks)。

这就可以了。当然,我们还可以通过可选参数来自定义订单:from_entry 将此平仓单关联到特定的入场订单;qtyqty_percent 指定平仓的数量或百分比;oca_name 将订单放入一个自定义的ORO组;commentcomment_profit 自定义订单注释;alert_messagealert_profit 自定义成交时的警报消息。

要创建一个基于价差的止盈单,我们为 profit 参数设定一个期望的盈利ticks数量。这是一个多头平仓的例子:

// 当价格上穿20周期EMA时,通过strategy.entry()开多仓。
// 同时,为该笔交易设置一个200个ticks的止盈目标。
if ta.crossover(close, ta.ema(close, 20))
	strategy.entry("Enter Long", strategy.long)
	strategy.exit("Exit Long", from_entry="Enter Long", profit=200)

这段代码在EMA金叉时,先用 strategy.entry() 开多仓,然后立刻用 strategy.exit() 为这个名为 “Enter Long” 的入场单设置一个止盈。因为我们同时提交了入场和出场指令,并用 from_entry 进行了关联,所以一旦入场单成交,这个止盈单就会立即被激活。

如果想自定义平仓数量,可以添加 qty 参数:

// 开仓10手
strategy.entry("Enter Long", strategy.long, qty=10)
// 设置止盈,但只平掉5手
strategy.exit("Exit Long", from_entry="Enter Long", profit=200, qty=5)

如果想在止盈成交时触发自定义警报,可以使用 alert_message

// 设置止盈,并自定义成交时的警报消息
strategy.exit("Exit Long", from_entry="Enter Long", profit=200, qty=5,
     alert_message="已触发200点止盈目标!")

要提交一个基于具体价格的止盈单,我们使用 limit 参数。这是一个空头交易的例子:

// 当价格下穿30周期SMA时,通过strategy.entry()开空仓。
// 并立即在当时K线最低价下方10%的位置设置一个止盈目标。
if ta.crossunder(close, ta.sma(close, 30))
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", limit=low * 0.90)

这里,我们用 limit 参数将止盈价格直接设定为 low * 0.90

同样,我们可以自定义平仓数量,例如平掉50%的仓位:

// 设置止盈,平掉50%的仓位
strategy.exit("Exit Short", from_entry="Enter Short", limit=low * 0.90,
     qty_percent=50)

也可以自定义警报消息,甚至可以在消息中包含动态的价格信息:

// 设置止盈,并自定义包含价格的警报消息
strategy.exit("Exit Short", from_entry="Enter Short", limit=low * 0.90,
     qty_percent=50, alert_message="已在 " + str.tostring(low * 0.90) + " 触发止盈!")

停止单(止损单)

要使用 strategy.exit() 生成一个止损单,我们的主要步骤是:通过函数的第一个参数设置订单ID,然后决定如何定义止损水平——基于价格用 stop 参数设置一个具体的止损价位,基于价差则用 loss 参数设置一个从开仓均价算起的亏损点数(ticks)。其他可选参数(如 from_entryqtycomment_loss 等)的用法与止盈单类似。

要生成一个基于价差的止损单,我们为 loss 参数设定可接受的最大亏损ticks数量。这是一个多头止损的例子:

// 当价格突破20周期最高点时,开多仓。
// 同时,为该笔交易设置一个300个ticks的止损。
if high > ta.highest(high, 20)[1]
	strategy.entry("Enter Long", strategy.long)
	strategy.exit("Exit Long", from_entry="Enter Long", loss=300)

这段代码在价格突破时开多仓,并立刻通过 strategy.exit() 提交一个关联的止损单,最大亏损设为300个ticks。

我们可以自定义止损数量,例如只对一半仓位进行止损:

// 开仓20手
strategy.entry("Enter Long", strategy.long, qty=20)
// 设置止损,但只平掉10手
strategy.exit("Exit Long", from_entry="Enter Long", loss=300, qty=10)

同样可以自定义止损触发时的警报消息:

// 设置止损,并自定义触发时的警报
strategy.exit("Exit Long", from_entry="Enter Long", loss=300, qty=10,
     alert_message="多头止损被触发!")

要在 strategy.exit() 中设置一个基于具体价格的止损,我们需要将其 stop 参数设置为那个目标价位。以一个空头交易为例:

// 当K线低点跌破20周期最低价时,通过 strategy.entry() 开立空头订单。
// 同时,为该订单提交一个设在K线最高价上方8%的止损单。
if low < ta.lowest(low, 20)[1]
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", stop=high * 1.08)

这个 if 语句判断当前K线的最低价是否低于过去20根K线的最低价。如果条件满足,strategy.entry() 函数会开立一个空头仓位(strategy.short)。

与此同时,strategy.exit() 函数为这个刚开的仓位生成一个止损单。这个平仓订单的名称是 “Exit Short”,并通过 from_entry 参数与名为 “Enter Short” 的开仓单进行绑定。stop 参数则将止损价格设在了当前K线最高价上涨8%的位置(high * 1.08)。

如果我们想指定平仓的数量,可以使用 qtyqty_percent 参数。示例如下:

// ...
if low < ta.lowest(low, 20)[1]
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", stop=high * 1.08,
		 qty_percent=80)

这段代码发送的空头止损单与之前类似,但新增的 qty_percent 参数会让 strategy.exit() 在触发时只平掉80%的仓位,而不是全部平仓。

如果策略使用了订单警报功能,并且警报模板中包含了 {{strategy.order.alert_message}} 占位符,那么我们可以通过 alert_messagealert_loss 参数来自定义止损触发时发送的警报消息。例如:

// ...
if low < ta.lowest(low, 20)[1]
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", stop=high * 1.08,
		 qty_percent=80, alert_message="空头止损触发 @ " + 
		 str.tostring(high * 1.08))

这里唯一的区别是 alert_message 参数,它指定了一个包含具体止损价格的自定义警报文本。由于止损价是数字,我们使用 str.tostring() 函数将其转换为文本。

移动止损订单(Trailing Stop)

要使用 strategy.exit() 函数来生成一个移动止损,我们需要完成几个步骤。第一步,通过函数的第一个参数为订单命名。第二步,选择激活条件,决定移动止损何时开始生效:按盈利点数激活,将 trail_points 参数设置为一个盈利的点数,当仓位浮动盈利达到这个点数时激活移动止损;按价格水平激活,为 trail_price 参数提供一个具体的价格,当市场价格触及这个价位时启用移动止损;立即激活,将 trail_points 参数设为0,一旦开仓订单成交,移动止损就会立刻开始工作。第三步,指定追踪距离,将 trail_offset 参数设置为移动止损应该距离最高价(多头)或最低价(空头)多少个点数,PineScript会根据这个距离自动调整止损位。

除了这三个核心步骤,我们还可以通过可选参数来自定义移动止损:from_entry 指定该止损应用于哪个开仓订单;qtyqty_percent 指定平仓的数量;oca_name 将该移动止损放入一个ORO订单组;commentcomment_trailing 自定义订单描述;alert_messagealert_trailing 自定义订单成交时的警报消息。

下面,我们来为三种不同的激活方式编写代码:按盈利激活、按价格激活,以及立即激活。

要创建一个在达到一定盈利后才激活的移动止损,我们需要将 trail_points 参数设置为盈利的点数,然后使用 trail_offset 参数来配置追踪的距离(同样以点数为单位)。以一个多头交易为例:

// 当RSI上穿30时,开立多头仓位,并为其设置一个移动止损。
// 该止损在盈利达到5个点时激活,并以20个点的距离跟随最高价。
if ta.crossover(ta.rsi(close, 7), 30)
	strategy.entry("Enter Long", strategy.long)
	strategy.exit("Exit Long", from_entry="Enter Long", trail_points=5,
		 trail_offset=20)

trail_points 参数告诉PineScript,当仓位盈利达到5个点时激活移动止损。而 trail_offset 则将止损距离设为20个点。

要自定义平仓数量,可以添加 qtyqty_percent 参数:

// ...
if ta.crossover(ta.rsi(close, 7), 30)
	strategy.entry("Enter Long", strategy.long, qty=10)
	strategy.exit("Exit Long", from_entry="Enter Long", trail_points=5,
		 trail_offset=20, qty=7)

这里,开仓数量为10手,而移动止损只平掉其中的7手。

要自定义警报消息,可以使用 alert_message 参数:

// ...
if ta.crossover(ta.rsi(close, 7), 30)
	strategy.entry("Enter Long", strategy.long, qty=10)
	strategy.exit("Exit Long", from_entry="Enter Long", trail_points=5,
		 trail_offset=20, qty=7, alert_message="20点移动止损触发!")

要创建一个在触及特定价格后才激活的移动止损,我们需要将 trail_price 参数设置为那个目标价位,然后通过 trail_offset 设置追踪距离。以一个空头交易为例:

// 当RSI下穿70时,开立空头仓位,并为其设置一个移动止损。
// 该止损在价格触及信号K线的最低价时激活,并以25个点的距离跟随最低价。
if ta.crossunder(ta.rsi(close, 7), 70)
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", trail_price=low,
		 trail_offset=25)

trail_price 参数告诉PineScript在价格达到 low(信号K线的最低价)时激活移动止损。激活后,止损位会始终保持在市场创下的新低点上方25个点的位置。

要设置部分平仓,可以使用 qty_percent

// ...
if ta.crossunder(ta.rsi(close, 7), 70)
	strategy.entry("Enter Short", strategy.short)
	strategy.exit("Exit Short", from_entry="Enter Short", trail_price=low,
		 trail_offset=25, qty_percent=50)

要创建一个立即激活的移动止损,我们只需将 trail_points 参数设为0,然后通过 trail_offset 指定追踪距离即可。以一个多头交易为例:

// 当RSI上穿70时,开立多头仓位,并立即为其设置一个
// 追踪距离为30个点的移动止损。
if ta.crossover(ta.rsi(close, 5), 70)
	strategy.entry("Enter Long", strategy.long)
	strategy.exit("Exit Long", from_entry="Enter Long", trail_points=0,
		 trail_offset=30)

trail_points 设为0即可实现移动止损的即时激活。

strategy.exit()的行为特性

了解 strategy.exit() 如何生成不同类型的订单后,我们再来看看它的一些重要行为特性。

首先是订单类型的限制。strategy.exit() 虽然灵活,但并不能创建所有类型的订单。它无法创建市价单——要通过市价单平仓,应使用 strategy.close()strategy.close_all() 函数;也无法创建止损限价单——要使用此类订单平仓,应使用 strategy.order() 函数。此外,strategy.exit() 不能同时发送一个固定价格的止损和一个移动止损。如果这样做,PineScript不会报错,但它只会放置那个距离当前市价更近的止损单。

其次,同一平仓指令不会重复执行。要实现分批平仓,策略必须多次调用 strategy.exit() 函数。这是因为,同一个平仓指令(由一次 strategy.exit() 调用生成)在部分成交后,不会被系统重新提交来平掉剩余的仓位。那条指令已经完成了它的使命,需要等待下一次新的开仓才能再次被激活。

这种设计是合理的,可以防止部分平仓单被反复触发。例如,如果我们有两个止盈目标,如果第一个目标成交后,该指令还被重复使用,那么仓位可能会在触及第二个目标前就被全部平掉。

因此,要实现多次平仓,我们需要为每一次平仓都调用一次 strategy.exit(),并使用不同的订单ID。例如,要实现两档止盈:

// ...
if ta.crossover(close, ta.ema(close, 20))
	strategy.entry("Enter Long", strategy.long)
	strategy.exit("Exit Long 1", from_entry="Enter Long", profit=10, qty_percent=50)
	strategy.exit("Exit Long 2", from_entry="Enter Long", profit=20)

第一个平仓单 “Exit Long 1” 在盈利10点时平掉50%的仓位。第二个 “Exit Long 2” 则在盈利20点时平掉剩余的仓位。

strategy.entry() 类似,我们可以通过 when 参数或 if 语句来条件性地执行 strategy.exit()。两者在功能上没有区别,但我个人更推荐使用 if 语句,因为它能让条件逻辑更突出,代码结构也更清晰易读。调用 strategy.exit() 时,也强烈建议使用关键字参数(如 profit=loss=),而不是依赖参数的位置,这会让代码更清晰,也更不容易出错。

最后还有几点一般特性值得知道:当 strategy.exit() 的触发条件变为 false 时,PineScript不会自动取消之前已生成的订单,要取消待处理的订单,必须明确调用 strategy.cancel()strategy.cancel_all()strategy.exit() 生成的每个订单都必须有唯一的订单ID,如果你用相同的ID调用两次 strategy.exit(),第二次调用只会修改第一次的订单,而不会创建新的平仓单。调用 strategy.exit() 时,必须至少指定一种平仓类型(profitlimitlossstop 或移动止损组合),否则会报错。它生成的所有订单都是模拟的,无法发送给真实的经纪商。

简单总结一下:strategy.exit() 函数用于模拟平仓操作。根据使用的参数,它可以生成三种订单类型:限价单(止盈)通过 profitlimit 参数设置;止损单通过 lossstop 参数设置;移动止损单通过 trail_pricetrail_pointstrail_offset 组合设置。strategy.exit() 生成的任何订单都必须与一个已有的开仓订单相关联,from_entry 参数用于指定该平仓单具体应用于哪个开仓订单。

用strategy.order()打开和关闭策略交易

TradingView策略通过模拟交易来评估历史信号的表现,而 strategy.order() 函数正是用于生成这些交易的买卖订单的核心工具。

更具体地说,strategy.order() 可以生成四种订单类型:市价单、止损单、限价单,以及止损限价单。

更重要的是,它的功能远不止于此,它能以多种方式来管理策略的仓位。上述四种订单类型中的任何一种,都可以实现:开立一个新仓位、对现有仓位进行加仓、平掉现有仓位的一部分、平掉策略的全部仓位,甚至将现有仓位反转为相反方向的仓位。

可见,strategy.order() 的功能非常强大!但你可能会问,既然已经有了其他几个各司其职的订单函数,为什么还要费心学习这个函数呢?

strategy.order() 的核心优势在于其灵活性。它能够生成所有类型的订单,并以6种不同的方式来操作仓位,这是其他任何订单函数都无法比拟的。此外,它不受策略的金字塔加仓设置和某些风险管理函数的限制,这使得它能在其他订单函数无法执行的场景下发挥作用。

现在,让我们开始深入学习 strategy.order() 函数的工作原理和用法。

标准语法格式

strategy.order() 函数的标准语法格式如下:

strategy.order(id, direction, qty, limit, stop, oca_name, oca_type,
     comment, when, alert_message)
  • id:订单的标识符。这是一个必需的字符串参数,用于为订单命名。我们可以通过这个ID在之后取消或修改待处理的订单,或者在订单成交后对其进行平仓。
  • direction:订单的方向。这个必需的参数只能是 strategy.long(生成一个买单)或 strategy.short(生成一个卖单)。
  • qty:一个数字,指定要交易的合约、股票或单位的数量。如果不设置此参数,将使用策略的默认订单大小;如果策略也未配置默认订单大小,则默认为1。
  • limit:订单的限价,需要一个具体的价格。如果设置了 limit,会生成一个限价单或止损限价单;如果不设置,则订单会是市价单或止损单。
  • stop:订单的止损触发价,也需要一个具体的价格。如果设置了 stop,会生成一个止损单或止损限价单;如果不设置,则订单会是市价单或限价单。
  • oca_name:一个字符串,指定订单所属的订单组名称。
  • oca_type:设置订单组的类型,可选值包括 strategy.oca.cancel(创建OCA组,One-Cancels-All,一成交则全取消)、strategy.oca.reduce(创建ORO组,One-Reduces-Others,一成交则减少其他)和 strategy.oca.none(默认值,不使用订单组)。
  • comment:一个字符串,用于为订单添加注释。这个注释会显示在图表和交易列表中。
  • when:一个布尔值,用于条件性地决定是否生成该订单。这是使用 if 语句来控制下单的另一种方式。
  • alert_message:一个字符串,用于设置订单的警报消息文本。当订单成交触发警报时,{{strategy.order.alert_message}} 这个占位符会被替换为这里设置的文本。

这么多参数,看起来是不是有点复杂?但别担心,我们很少会同时使用所有参数,这使得 strategy.order() 在实际应用中比看起来要简单得多。但在我们开始看代码之前,先来理解这个函数的一个核心特性。

strategy.order()发送的是买单和卖单,而非多头和空头订单

strategy.order() 函数生成的是买单(Buy Order)和卖单(Sell Order),它并不直接生成我们通常理解的多头(Long)和空头(Short)订单。

为了理解这一点,我们可以将其与 strategy.entry() 函数进行对比。strategy.entry() 生成的确实是明确的多头和空头订单。当我们使用 strategy.entry() 时,我们很清楚:一个多头订单成交后,策略就会持有多头仓位;一个空头订单成交后,策略就会持有空头仓位。

但对于 strategy.order(),情况并非如此。当这个函数执行一个订单时,我们只知道它买入或卖出了某个东西。这个行为最终对仓位产生何种影响,完全取决于执行订单时策略的当前持仓状态。

一个买单成交后,可能会导致以下任何一种结果:开立一个新的多头仓位(如果当时策略空仓)、对已有的多头仓位进行加仓、平掉一个空头仓位、部分平掉一个空头仓位,或将一个空头仓位反转为多头仓位。

同样,一个卖单成交后,也可能开立一个新的空头仓位、对已有的空头仓位加仓、平掉一个多头仓位、部分平掉一个多头仓位,或将一个多头仓位反转为空头仓位。

不巧的是,strategy.order() 函数使用 strategy.long 来生成买单,用 strategy.short 来生成卖单。这种命名方式其实并不恰当,很容易引起误解。我们必须清楚地认识到:当 strategy.order() 使用 strategy.long 时,它生成的不是一个多头订单,而是一个买单,我们无法保证这个订单成交后策略的仓位就一定是多头;当使用 strategy.short 时同理,它生成的是一个卖单,成交后策略的仓位也不一定是空头。

strategy.order()的示例

理解了 strategy.order() 的核心理论后,让我们正式进入代码示例。

市价单(Market Order)

要使用 strategy.order() 生成一个市价单,我们只需做两件事:用第一个参数定义订单ID,用第二个参数设置订单方向(strategy.long 用于买单,strategy.short 用于卖单)。当然,我们也可以通过 qtycommentalert_message 等可选参数来自定义订单。

先看买入市价单:

// 当价格突破20周期最高价时,提交一个买单
if high > ta.highest(high, 20)[1]
	strategy.order("Buy (Market)", strategy.long)

这个 if 语句判断当前K线的最高价是否突破了近期的高点。如果突破,strategy.order() 就会执行,生成一个ID为 “Buy (Market)” 的买单。

要指定订单数量和警报消息:

// ...
if high > ta.highest(high, 20)[1]
	strategy.order("Buy (Market)", strategy.long, qty=5,
		 alert_message="市价买入5手合约")

再看卖出市价单:

// 当价格跌破20周期最低价时,提交一个卖单
if low < ta.lowest(low, 20)[1]
	strategy.order("Sell (Market)", strategy.short)

这个 if 语句判断当前K线的最低价是否跌破了近期的低点。如果跌破,就生成一个ID为 “Sell (Market)” 的卖单。

同样,我们也可以为其添加数量和警报消息:

// ...
if low < ta.lowest(low, 20)[1]
	strategy.order("Sell (Market)", strategy.short, qty=20,
		 alert_message="市价卖出20手合约")

限价单(Limit Order)

要让 strategy.order() 生成一个限价单,我们需要:设置订单ID,设置订单方向,再通过 limit 参数定义限价(这需要一个具体的价格)。同样,也可以为其添加 qtyoca_typeoca_namecommentalert_message 等可选参数。

买入限价单:

// 当价格穿越12周期SMA时,在前一根K线的低点挂一个买入限价单
if ta.cross(close, ta.sma(close, 12))
	strategy.order("Buy (Limit)", strategy.long, limit=low[1])

要指定数量并加入一个ORO订单组(一成交则减少其他):

// ...
if ta.cross(close, ta.sma(close, 12))
	strategy.order("Buy (Limit)", strategy.long, limit=low[1], qty=20,
		 oca_name="cross-entry", oca_type=strategy.oca.reduce)

当这个20手的买单成交后,TradingView会自动将 “cross-entry” 组内其他待处理订单的数量减少20手。

卖出限价单:

// 当价格穿越30周期EMA时,在前一根K线的高点挂一个卖出限价单
if ta.cross(close, ta.ema(close, 30))
	strategy.order("Sell (Limit)", strategy.short, limit=high[1])

要指定数量和警报消息:

// ...
if ta.cross(close, ta.ema(close, 30))
	strategy.order("Sell (Limit)", strategy.short, limit=high[1], qty=25,
		 alert_message="在 " + str.tostring(high[1]) + " 价格卖出25手合约")

停止单(Stop Order)

要使用 strategy.order() 函数来生成一个停止单,我们需要完成三件事:通过函数的第一个参数设置订单ID;通过第二个参数定义订单的交易方向,strategy.long 用于买入停止单(Buy Stop),strategy.short 用于卖出停止单(Sell Stop);再通过 stop 参数指定订单的触发价格(注意这是具体的价位,而非价差)。

要生成一个基本的停止单,以上参数就足够了。但如果需要,我们还可以通过可选参数来自定义订单:qty 设置订单数量以覆盖策略的默认订单大小;oca_typeoca_name 定义一个订单关联组;comment 为订单添加自定义的图表注释;alert_message 设定订单成交时触发的自定义警报消息。我们一般不将其他参数与停止单一起使用。下面来看代码。

买入停止单的写法如下:

// 当收盘价低于过去20根K线的最低收盘价时,
// 在前一根K线的最高价位置提交一个买入停止单
if close < ta.lowest(close, 20)[1]
	strategy.order("Buy (Stop)", strategy.long, stop=high[1])

这个 if 语句判断当前收盘价是否低于过去20根K线的最低收盘价。如果条件成立,便提交一个ID为 “Buy (Stop)” 的买入停止单,其触发价格为前一根K线的最高价(high[1])。

我们可以通过添加其他参数来进一步自定义这个订单。例如,使用 qty 来设定其数量:

// 提交一个数量为5手的买入停止单
strategy.order("Buy (Stop)", strategy.long, stop=high[1], qty=5)

通过 alert_message 来自定义成交时的警报消息(它会替换警报设置中的 {{strategy.order.alert_message}} 占位符):

// 提交买入停止单,并自定义警报消息
strategy.order("Buy (Stop)", strategy.long, stop=high[1], qty=5,
     alert_message="已通过停止单在 " + str.tostring(high[1]) + " 买入5手")

通过 oca_typeoca_name 将订单加入一个OCA(One-Cancels-All,一成交全撤销)组:

// 提交买入停止单,并将其加入一个名为 "stop-entry" 的 OCA 组
strategy.order("Buy (Stop)", strategy.long, stop=high[1],
     oca_type=strategy.oca.cancel, oca_name="stop-entry")

将订单放入OCA组后,当这个组内的任何一个订单成交时,TradingView会自动取消同组的其他所有挂单,这有助于防止策略意外地成交过多订单。

卖出停止单的写法如下:

// 如果收盘价高于过去20根K线的最高收盘价,
// 在前一根K线的最低价位置提交一个卖出停止单
if close > ta.highest(close, 20)[1]
	strategy.order("Sell (Stop)", strategy.short, stop=low[1])

这段代码判断当前收盘价是否高于过去20根K线的最高收盘价。如果成立,就提交一个ID为 “Sell (Stop)” 的卖出停止单,触发价格为前一根K线的最低价(low[1])。

同样,我们也可以自定义其数量、警报消息或将其加入OCA组:

// 设置数量为220股
strategy.order("Sell (Stop)", strategy.short, stop=low[1], qty=220)

// 自定义警报消息
strategy.order("Sell (Stop)", strategy.short, stop=low[1], qty=220,
     alert_message="已通过停止单在 " + str.tostring(low[1]) + " 卖出220股")

// 加入OCA组
strategy.order("Sell (Stop)", strategy.short, stop=low[1],
     oca_type=strategy.oca.cancel, oca_name="stop-entry")

停止限价单(Stop-Limit Order)

strategy.order() 函数能生成的第四种也是最后一种订单是停止限价单。创建这种订单需要四个必需的参数:订单ID(函数第一个参数)、交易方向(第二个参数,strategy.longstrategy.short)、限价(limit)和停止价(stop,即订单的触发价格)。limitstop 都需要是具体的价位,而不是价差。其他可选参数的用法与停止单类似。

要生成一个买入停止限价单,我们可以这样使用 strategy.order()

// 当RSI上穿80时,提交一个停止-限价单:
// 触发价为收盘价上方2%,限价为最高价上方5个最小变动单位
if ta.crossover(ta.rsi(close, 9), 80)
	strategy.order("Buy (Stop-Limit)", strategy.long, stop=close * 1.02,
		 limit=high + 5 * syminfo.mintick)

当RSI上穿80时,代码提交一个买入停止限价单。其 stop 价格(触发价)设在收盘价上方2%的位置,而 limit 价格(最终成交的最高可接受价格)则设在K线最高价上方5个最小变动单位处。

我们可以通过添加 qtyalert_message 和OCA参数来进一步自定义它:

// 自定义数量、警报和OCA组
strategy.order("Buy (Stop-Limit)", strategy.long, stop=close * 1.02,
     limit=high + 5 * syminfo.mintick, qty=300,
     alert_message="已通过停止-限价单买入300手",
     oca_type=strategy.oca.cancel, oca_name="buy-stop-entry")

要生成一个卖出停止限价单,代码如下:

// 当RSI下穿20时,提交一个停止-限价单:
// 触发价为收盘价下方2%,限价为最低价下方5个最小变动单位
if ta.crossunder(ta.rsi(close, 5), 20)
	strategy.order("Sell (Stop-Limit)", strategy.short, stop=close * 0.98,
		 limit=low - 5 * syminfo.mintick)

当RSI下穿20时,代码提交一个卖出停止限价单。其 stop 价格设在收盘价下方2%的位置,而 limit 价格(最终成交的最低可接受价格)则设在K线最低价下方5个最小变动单位处。

同样,我们也可以自定义其数量、警报和OCA组:

// 自定义数量、警报和OCA组
strategy.order("Sell (Stop-Limit)", strategy.short, stop=close * 0.98,
     limit=low - 5 * syminfo.mintick, qty=8, 
     alert_message="已通过停止-限价单卖出8手",
     oca_type=strategy.oca.cancel, oca_name="sell-stop-entry")

strategy.order()的特性

现在我们知道了如何在代码中使用 strategy.order(),让我们来总结一下这个函数的一些重要特性。

首先是与策略持仓状态互动。strategy.order() 函数如何改变策略的仓位,完全取决于它被调用时的初始持仓状态。例如,当策略持有多仓时,一个卖出指令会平掉(部分或全部)多仓;但如果策略是空仓,同一个卖出指令则会开启一个新的空仓。

一个买入指令,根据持仓状态,可能导致以下5种结果之一:开立新多仓、加仓多头、部分平掉空仓、全部平掉空仓、反手开多仓。同样,一个卖出指令,可能导致开立新空仓、加仓空头、部分平掉多仓、全部平掉多仓,或反手开空仓。

其次,平仓订单需要指定数量。当 strategy.order() 不设置 qty 参数时,它会使用策略的默认订单大小(通常是1)。对于开仓来说这问题不大,但对于平仓,我们几乎总是需要明确指定其数量。否则,你可能会发现订单没有完全平掉仓位,或者平得过多,意外地开了一个反向仓位。

在以下几种情况,明确指定平仓数量尤为重要:策略经过多次加仓,需要一次性平掉所有仓位;策略希望分批次减仓;开仓时使用了自定义的数量,平仓时需要与之匹配;策略的默认订单大小是基于资金或权益百分比计算的,这会导致开仓和平仓时计算出的数量不同。

与其他订单函数不同,strategy.order() 不会自动平掉整个仓位。因此,在使用它进行平仓时,通过 qty 参数来配置订单数量是常规操作,而非特例。

那么,如何用 strategy.order() 来确保平掉整个仓位呢?我们可以使用内置变量 strategy.position_size:平多仓时,将卖出订单的 qty 设为 strategy.position_size;平空仓时,将买入订单的 qty 设为 math.abs(strategy.position_size)

strategy.order() 函数还可以实现仓位的反转。要实现反手,提交的订单数量必须大于当前的持仓量。多翻空时,订单数量等于 strategy.position_size(当前多仓数量)加上期望的新空仓数量,例如 qty = strategy.position_size + 5;空翻多时,订单数量等于 math.abs(strategy.position_size)(当前空仓数量的绝对值)加上期望的新多仓数量,例如 qty = math.abs(strategy.position_size) + 200

要注意的是:反手功能虽然强大,但也伴随着风险。如果数量计算不当,本意是平仓的指令可能会意外地开立一个反向仓位。

与其它订单函数一样,我们可以通过 when 参数或 if 语句来控制 strategy.order() 的执行时机:

// 使用 when 参数
strategy.order("Enter Long", strategy.long, when=ta.crossover(close, ta.sma(close, 20)))

// 使用 if 语句
if ta.crossover(close, ta.sma(close, 20))
	strategy.order("Enter Long", strategy.long)

两种写法的效果完全相同。我个人更推荐使用 if 语句,因为它能让条件逻辑更突出,代码结构也更清晰易读。

调用函数时,我也强烈建议使用关键字参数,而不是依赖参数的固定顺序(位置参数):

// 不推荐:使用位置参数,可读性差且容易出错
strategy.order("Buy (Limit)", strategy.long, na, low[1], na, "", strategy.oca.none, "", true, "B,EURUSD,1")

// ✅ 推荐:使用关键字参数,清晰、简洁且不易出错
strategy.order("Buy (Limit)", strategy.long, limit=low[1], alert_message="B,EURUSD,1")

最后还有几点:strategy.order() 虽然灵活,但它的执行仍会受到策略中风险管理函数和保证金设置的限制;它生成的所有订单都是模拟的,不能用于实盘交易或管理手动开立的仓位;它的灵活性也带来了更高的复杂性和出错的可能性,因此在我看来,它并非一个适合在所有场景下都频繁使用的理想函数——对于单纯的开仓和平仓,strategy.entry()strategy.exit()strategy.close() 通常是更安全、更明确的选择。

简单总结一下:strategy.order() 是一个通用的订单函数,可以生成市价、停止、限价和停止限价订单。它可以生成买入(strategy.long)和卖出(strategy.short)指令,最终效果取决于它被调用时策略的持仓状态。它比其他订单函数更灵活,但也更复杂,需要谨慎使用,尤其是平仓时的数量控制。

赞(0)
未经允许不得转载:图道交易 » Pine Script(216):strategy.exit()与strategy.order()
分享到

评论 抢沙发

登录

找回密码

注册