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

Pine Script(247):在连败或连胜后停止TradingView策略

#Pine Script入门教学

如何在连续n次亏损后停止TradingView策略?

无论一个交易策略多么优秀,遭遇一连串的连续亏损都只是时间问题。这种情况不仅会损害我们的账户回报,也会极大地打击我们的交易信心。管理这种风险的有效方法之一,便是在发生极端的连败时,果断停止交易。本文将详细介绍如何在TradingView Pine中,从零开始编写这一风控逻辑。

限制交易风险:基于连续亏损笔数停止交易

在经历一轮持续的连败时,一个交易策略似乎无论发出什么信号,结果都是错的。尽管我们的第一反应可能是继续交易以挽回损失,但连败本身已经是一个强烈的信号,说明策略可能暂时与当前市场水土不服。

在TradingView Pine中,有几种方法可以管理连败。其中一种是使用内置的 strategy.risk.max_cons_loss_days() 函数,它会在策略达到设定的连续亏损天数后停止交易。但该函数是基于策略权益而非已平仓交易来衡量亏损的。这意味着一笔盈利的交易,仅仅因为其浮动利润回吐,也可能被计为一个亏损日。另一个更贴近交易者直觉的选项,是基于已平仓交易的实际表现来管理连败。接下来,让我们看看如何自己动手在TradingView中实现这一逻辑。

要让我们的TradingView策略能够基于连续亏损的交易笔数来停止运行,我们需要遵循以下六个步骤:第一步(可选),创建一个输入项,用于灵活配置最大可容忍的连败长度;第二步,判断是否有新的亏损交易发生,从而可能增加连败计数;第三步,计算当前连续亏损的交易笔数;第四步,检查当前的连败长度是否仍在允许的范围内;第五步,只要未达到最大连败上限,就正常提交入场交易;第六步,一旦达到连败上限,立即平掉所有当前持有的仓位。让我们来详细分解这些步骤及其所需的代码。

步骤1.(可选)通过输入项设置连败长度

为了方便地调整最大连败长度,我们可以创建一个(可选的)输入选项:

// 通过输入项来设置最大连败长度
maxLosingStreak = input.int(15, title="最大连败长度", minval=1)

这里,我们使用 input.int() 函数创建了一个整数输入框。我们将其默认值设为15,并通过 minval=1 参数确保输入值始终为正数,这有助于简化后续步骤中的代码逻辑。

我们将这个输入值存入 maxLosingStreak 变量。(如果你的策略不需要输入项,只需在后续步骤中,将 maxLosingStreak 理解为我们设定的固定最大连败长度即可。)

步骤2. 判断是否有新的亏损交易增加了连败计数

接下来,我们需要判断策略是否刚刚完成了一笔亏损的平仓。实现方式有多种,但最直接的方法是利用 strategy.losstrades 变量,该变量返回策略的累计亏损交易总数。

但要延长一次连败,期间不能有任何盈利或保本的交易,因为它们会重置连败计数。因此,我们还需要同时检查 strategy.wintradesstrategy.eventrades

// 检查是否刚刚发生了一笔新的、会增长连败计数的亏损交易
newLoss = strategy.losstrades > strategy.losstrades[1] and
     strategy.wintrades == strategy.wintrades[1] and
     strategy.eventrades == strategy.eventrades[1]

这个布尔变量 newLoss 的值由三个表达式通过 and 运算符组合而成。这意味着,只有当所有三个条件都为 true 时,newLoss 才为 true。首先,我们判断累计亏损笔数(strategy.losstrades)是否比前一根K线增加了,如果是,说明刚有一笔亏损交易平仓。但这还不够,我们还需确保累计盈利笔数(strategy.wintrades)与前一根K线保持不变,同时,累计保本笔数(strategy.eventrades)也必须保持不变。

只有当这三个条件同时满足时,我们才能确定,刚刚发生的这笔亏损交易,是开启或延长了一次连败。(尽管一笔盈利和一笔亏损交易在同一根K线上平仓的可能性不大,但如果K线范围极宽且我们有分批平仓的逻辑,这种情况也是可能发生的。)

请注意,strategy.losstradesstrategy.wintradesstrategy.eventrades 这些变量已经考虑了策略的佣金和滑点。这使它们成为衡量策略连败的可靠工具。

步骤3. 计算当前的连败长度

接下来,我们来确定策略当前的连败长度:

// 确定当前的连败长度
streakLen = 0

streakLen := if newLoss
    nz(streakLen[1]) + 1
else
    if strategy.wintrades > strategy.wintrades[1] or
         strategy.eventrades > strategy.eventrades[1]
        0
    else
        nz(streakLen[1])

这里,我们首先声明了一个持久变量 streakLen。然后,一个 if/else 结构赋予了它实际的逻辑。如果步骤2中的 newLosstrue,说明连败正在持续,我们便取出 streakLen 在前一根K线的值,并加1。我们用 nz() 函数包裹 streakLen[1],以防止在脚本开始时因没有历史值而出现错误。如果 newLossfalse,则进入嵌套的 if/else:如果发生了盈利或保本交易,说明连败被中断,我们将 streakLen 重置为0;如果没有新的亏损,也没有新的盈利或保本,说明连败状态未变(例如,无交易发生),我们便让 streakLen 保持其前一根K线的值。

步骤4. 检查连败是否仍在允许范围内

知道了当前的连败长度后,我们就可以判断是否仍在可容忍的范围内:

// 检查连败长度是否仍在允许的最大值之内
okToTrade = streakLen < maxLosingStreak

这里,我们将步骤3中计算出的 streakLen 与我们设定的最大值 maxLosingStreak 进行比较。如果小于上限,则布尔变量 okToTradetrue;否则为 false

这个 okToTrade 变量将成为我们策略的总开关,告诉我们脚本是应该继续交易(true),还是应该暂停(false)。

步骤5. 在达到上限前正常提交入场订单

现在,我们将 okToTrade 这个总开关整合到策略的入场逻辑中。示例如下:

// 在考虑当前连败的情况下提交订单
if okToTrade and enterLong
    strategy.entry("EL", strategy.long)

if okToTrade and enterShort
    strategy.entry("ES", strategy.short)

在执行 strategy.entry() 之前,我们先检查 okToTrade 是否为 true。只有在连败未达上限时,我们才允许策略根据其原有的 enterLongenterShort 信号建立新的仓位。

一旦策略达到最大连败数,okToTrade 将变为 false,这将导致 if 条件不成立,从而有效阻止策略建立任何新的交易。

请注意,此步骤中的 enterLongenterShort 仅为示例占位符。你需要将 okToTrade 整合到你自己策略的实际入场条件中。核心思想不变:在任何 strategy.entry()strategy.order() 函数执行开仓操作前,都必须先检查 okToTrade 的状态。

步骤6. 达到连败上限时平掉所有持仓

最后一步,我们让策略在达到最大连败上限时,立即平掉所有当前持有的仓位。这实际上就完全停止了策略的后续活动。

// 当达到最大连败时,平掉所有仓位
if not okToTrade
    strategy.close_all()

这个 if 语句使用 not 逻辑运算符来判断 okToTrade 是否为 false。如果是,就意味着策略已触及最大连败数。

此时,我们调用 strategy.close_all() 函数,它会通过一笔市价单来平掉所有当前持仓。(如果策略当时没有持仓,strategy.close_all() 则不执行任何操作。)

需要注意的一点是:strategy.close_all() 发送的这笔市价平仓单,如果运气不佳,其本身也可能是一笔亏损交易。在这种情况下,最终记录的连败长度会比我们设定的最大值多1。

示例策略:在一连串亏损后停止交易

下方的脚本将以上六个步骤整合进了一个完整的策略中。该策略基于两条移动平均线的交叉进行交易。当快线上穿慢线时做多,反之则做空。

这个过程会一直持续,直到策略触及最大连续亏损的上限。一旦发生这种情况,策略将不再发送任何新的入场订单,并会平掉所有当前持仓,从而有效地停止策略。完整的策略代码如下:

//@version=5
strategy(title="在连败后停止", overlay=false, precision=0,
     default_qty_type=strategy.fixed, default_qty_value=5)

// 步骤1:
// 通过输入项来设置最大连败长度
maxLosingStreak = input.int(15, title="最大连败长度", minval=1)

// 计算移动平均线
fastMA = ta.ema(close, 5)
slowMA = ta.ema(close, 25)

// 定义交易条件
enterLong  = ta.crossover(fastMA, slowMA)
enterShort = ta.crossunder(fastMA, slowMA)

// 步骤2:
// 检查是否刚刚发生了一笔新的、会增长连败计数的亏损交易
newLoss = strategy.losstrades > strategy.losstrades[1] and
     strategy.wintrades == strategy.wintrades[1] and
     strategy.eventrades == strategy.eventrades[1]

// 步骤3:
// 确定当前的连败长度
streakLen = 0

streakLen := if newLoss
    nz(streakLen[1]) + 1
else
    if strategy.wintrades > strategy.wintrades[1] or
         strategy.eventrades > strategy.eventrades[1]
        0
    else
        nz(streakLen[1])

// 在图表上显示当前的连败长度及其上限
plot(streakLen, style=plot.style_columns,
     color=streakLen < maxLosingStreak ? color.maroon : color.red)
bgcolor(newLoss ? color.new(color.red, 80) : na)
hline(maxLosingStreak, color=color.red, linestyle=hline.style_solid,
     linewidth=2)
hline(0, linestyle=hline.style_solid, color=color.gray)

// 步骤4:
// 检查连败长度是否仍在允许的最大值之内
okToTrade = streakLen < maxLosingStreak

// 步骤5:
// 在考虑当前连败的情况下提交订单
if okToTrade and enterLong
    strategy.entry("EL", strategy.long)

if okToTrade and enterShort
    strategy.entry("ES", strategy.short)

// 步骤6:
// 当达到最大连败时,平掉所有仓位
if not okToTrade
    strategy.close_all()

该策略会将连败长度绘制出来,以便轻松追踪这一风险指标。在下方的以太坊图表示例中,策略的连败长度缓慢增长至其设定的最大值6。当达到该值时,策略提交了一笔市价单来平掉当前的持仓。不幸的是,这笔平仓交易本身也是亏损的。因此,策略最终以连续7次亏损而停止。

一旦策略达到其最大连续亏损数,它将在剩余的回测期间以及此后的所有实时信号中完全停止。这样,策略的亏损就不会再恶化,但这也意味着策略失去了任何恢复的机会。

要了解更多管理TradingView策略风险的方法,请参阅风险管理分类下的文章。

根据连续获胜次数停止TradingView策略

交易中最大的风险无疑是重大亏损。但一个策略突然表现得异常出色,甚至远超历史回测,这同样值得警惕。一波超长的连胜可能只是运气,但也可能预示着市场发生了根本性变化,使得原有的策略逻辑不再可靠。让我们来看看如何根据这种异常的连胜纪录来暂停一个TradingView策略的交易。

防止非理性繁荣:通过连胜纪录停止交易

极端连胜的问题在于,它们迟早会以一连串的亏损告终。并且,一个远超回测结果的连胜纪录本身也暗示着问题:这意味着该策略未来的连亏纪录很可能也会比历史表现更差。

TradingView标准的风险函数主要用于处理亏损,例如 strategy.risk.max_cons_loss_days() 可以限制策略的连续亏损天数。但对于那些表现好到出乎意料的策略,却没有现成的工具。幸运的是,只需几个步骤,我们就可以编写自定义的Pine Script代码来管理这种连胜风险。

要让我们的TradingView策略在达到设定的连胜次数后自动停止交易,需要完成以下任务:第一步(可选),使用输入选项来设定最大连胜长度;第二步,检测策略是否刚刚平掉一笔盈利的交易,并且这笔交易延续了连胜纪录;第三步,计算当前的连续盈利交易次数;第四步,将当前的连胜次数与设定的上限进行比较;第五步,仅在连胜次数未达到上限时才提交新的开仓订单;第六步,一旦达到最大连胜次数,立即平掉所有当前持有的仓位。让我们逐一分解这些步骤,并看看需要哪些代码来实现。

步骤1.(可选)使用输入选项设定最大连胜长度

为了方便地调整最大连胜次数,我们可以创建一个输入选项,这样就不必每次都去修改代码。实现代码如下:

// 使用输入项来设定最大连胜长度
maxWinStreak = input.int(15, title="最大连胜长度", minval=1)

我们使用 input.int() 函数来创建一个整数输入框,默认值为15。通过 minval 参数,我们确保这个值至少为1,这为我们后续的代码逻辑提供了保障。我们将这个输入值保存在 maxWinStreak 变量中。(如果你不需要输入选项,只需记住,后续步骤中使用的 maxWinStreak 变量即代表你设定的最大连胜次数。)

步骤2. 判断是否产生了一笔干净的盈利

我们可以用多种方法来判断一笔交易是否盈利。一个简单且可靠的方法是利用 strategy.wintrades 变量,它返回策略的盈利交易总数。如果这个变量相较于上一根K线增加了,说明至少有一笔盈利交易被平仓。

但为了精确地追踪连胜,我们不仅要检查盈利交易数(strategy.wintrades)是否增加,还要同时确认亏损交易数(strategy.losstrades)和保本交易数(strategy.eventrades)均未增加。只有这样,我们才能确定这笔盈利确实是延续了连胜,而不是发生在一次盈亏交替中。

// 判断是否有一笔新的、能够延续连胜的盈利交易产生
newWin = strategy.wintrades > strategy.wintrades[1] and
     strategy.losstrades == strategy.losstrades[1] and
     strategy.eventrades == strategy.eventrades[1]

newWin 变量只有在上述三个条件同时满足时才为 true

重要提示:TradingView的 strategy.wintradesstrategy.losstradesstrategy.eventrades 变量在计算时已包含了佣金和滑点成本,这使得它们成为衡量策略连胜长度的可靠依据。

步骤3. 计算当前连胜长度

现在,我们有了一个能判断干净盈利是否发生的变量。我们可以利用它来计算当前的连胜长度:

// 计算当前的连胜长度
streakLen = 0

streakLen := if newWin
    nz(streakLen[1]) + 1
else
    if strategy.losstrades > strategy.losstrades[1] or
         strategy.eventrades > strategy.eventrades[1]
        0
    else
        nz(streakLen[1])

我们定义了一个 streakLen 变量来记录连胜长度。其更新逻辑是:如果 newWintrue,说明产生了一笔新的连胜,我们将 streakLen 的前一个值加1;否则,如果发生了亏损或保本交易,说明连胜中断,我们将 streakLen 重置为0;如果以上情况都未发生(即没有新的平仓),则 streakLen 的值保持不变。(nz() 函数用于处理变量在初始时可能为空(na)的情况,将其视作0来计算。)

步骤4. 判断是否已达到最大连胜上限

有了当前的连胜长度,我们就可以将其与设定的上限进行比较了:

// 判断连胜次数是否仍在限制范围内
okToTrade = streakLen < maxWinStreak

okToTrade 是一个布尔变量。如果当前的连胜长度 streakLen 小于我们设定的上限 maxWinStreak(默认为15),okToTrade 就为 true,否则为 false

步骤5. 根据连胜状态决定是否开仓

现在,我们有了一个明确的交易开关(okToTrade)。我们只需在提交开仓订单前检查这个变量即可:

// 仅当未达到最大连胜上限时才提交订单
if okToTrade and enterLong
    strategy.entry("EL", strategy.long)
if okToTrade and enterShort
    strategy.entry("ES", strategy.short)

当策略达到最大连胜上限时,okToTrade 会变为 false,从而阻止策略提交任何新的开仓订单。

重要提示:请确保在你策略中所有可能产生新仓位的代码(包括 strategy.entry()strategy.order())前都加入了 okToTrade 这个条件判断,否则此风控逻辑将无法生效。

步骤6. 达到上限后立即平仓

最后一步,一旦达到最大连胜次数,我们就平掉所有剩余的持仓,从而彻底停止策略的交易活动。

// 一旦达到最大连胜,立即清空所有仓位
if not okToTrade
    strategy.close_all()

我们使用 not okToTrade 来判断 okToTrade 是否为 false。一旦为 false,就调用 strategy.close_all() 函数,它会发送一个市价单来平掉所有当前持仓。

请注意,strategy.close_all() 发出的市价平仓单本身也可能是一笔盈利的交易。如果发生这种情况,最终的连胜次数会比我们设定的最大值多一次。

示例策略:实现连胜后停止交易

下方的完整策略脚本融合了以上所有步骤。它基于均线交叉进行交易,并带有止损和止盈。但核心在于,当策略达到设定的最大连胜次数后,交易将完全停止。

//@version=5
strategy(title="连胜后停止",
     overlay=false, precision=0)

// 步骤 1: 设定最大连胜长度
maxWinStreak = input.int(15, title="最大连胜长度", minval=1)

// 计算指标和交易条件
fastMA = ta.ema(close, 5)
slowMA = ta.ema(close, 25)
isFlat     = strategy.position_size == 0
enterLong  = isFlat and ta.crossover(fastMA, slowMA)
enterShort = isFlat and ta.crossunder(fastMA, slowMA)

// 步骤 2: 判断是否为“干净”的盈利
newWin = strategy.wintrades > strategy.wintrades[1] and
     strategy.losstrades == strategy.losstrades[1] and
     strategy.eventrades == strategy.eventrades[1]

// 步骤 3: 计算连胜长度
streakLen = 0
streakLen := if newWin
    nz(streakLen[1]) + 1
else
    if strategy.losstrades > strategy.losstrades[1] or
         strategy.eventrades > strategy.eventrades[1]
        0
    else
        nz(streakLen[1])

// 在图表上可视化连胜状态
plot(streakLen, style=plot.style_columns,
     color=streakLen < maxWinStreak ? color.green : color.gray)
bgcolor(newWin ? color.lime : na)
hline(maxWinStreak, color=color.orange, linestyle=hline.style_solid,
     linewidth=2)
hline(0, linestyle=hline.style_solid, color=color.gray)

// 步骤 4: 判断是否可以交易
okToTrade = streakLen < maxWinStreak

// 步骤 5: 提交开仓订单
if okToTrade and enterLong
    strategy.entry("EL", strategy.long)
if okToTrade and enterShort
    strategy.entry("ES", strategy.short)

// 计算并设置止盈止损
highestHighLong = ta.highest(high, 15)[1]
lowestLowLong   = ta.lowest(low, 30)[1]
highestHighShort = ta.highest(high, 30)[1]
lowestLowShort   = ta.lowest(low, 15)[1]
if strategy.position_size > 0
    strategy.exit("XL", stop=lowestLowLong, limit=highestHighLong)
if strategy.position_size < 0
    strategy.exit("XS", stop=highestHighShort, limit=lowestLowShort)

// 步骤 6: 达到上限后平仓
if not okToTrade
    strategy.close_all()

这个策略还会将当前的连胜次数绘制成柱状图。在下方的EUR/USD图表中,我们可以看到连胜次数随着交易的进行而缓慢增加。当最后一笔空头交易以盈利平仓后,策略达到了15次连胜的上限。从那一刻起,便再也没有新的交易产生。

要触发止损,必须是一系列连续的盈利。任何一次亏损都足以将连胜计数器重置为零,从而给策略更多的时间去交易。在下方的SPY(标普500 ETF)图表中,策略最初取得了3连胜,但随后的一次亏损将连胜纪录清零了。

管理交易连续性的另一种思路是在连续n次亏损后停止交易。要了解更多管理TradingView策略风险的方法,可以参阅相关的风险管理主题分类。

赞(0)
未经允许不得转载:图道交易 » Pine Script(247):在连败或连胜后停止TradingView策略
分享到

评论 抢沙发

登录

找回密码

注册