停止基于每周亏损交易的TradingView策略
好的策略不仅能发出盈利信号,还应在市场状况不利时有效控制亏损。一种在环境不佳时避免交易的有效方法,就是限制每周的亏损交易笔数。一旦触及这个上限,便暂停本周的交易,待下周再战。本文将详细介绍如何在TradingView中编写这一逻辑。
基于每周亏损交易笔数来停止策略
一连串的连续亏损对任何交易系统都是一个严峻的风险。为了应对这种风险,我们可以随着连亏的持续而减小交易规模,或者干脆暂停交易一段时间,待时机好转再入场。后者的一个具体实践,便是限制每周的亏损交易次数。
当我们的交易策略设置了每周亏损笔数的上限时,它便可以随心所欲地捕捉盈利信号。但一旦亏损交易的次数累积到某个水平,我们便将策略冷冻起来,直到下一周。让我们来看看如何通过TradingView代码来实现这一功能。
请注意,TradingView虽然提供了一个内置函数 strategy.risk.max_cons_loss_days() 来限制策略的连续亏损天数,但我们在本文中编写的代码更为灵活。内置函数一旦触发,便无法自动重新激活策略,并且它是基于策略权益而非已平仓的亏损交易来衡量亏损的。
要让我们的交易策略能基于每周的亏损交易笔数来停止运行,我们将实施以下六个步骤:第一步(可选),创建一个输入项,用于灵活配置每周的最大亏损交易笔数;第二步,获取策略在上周末时,已累计的亏损交易总数;第三步,确定本周内发生的总亏损交易笔数;第四步,将本周的亏损笔数与设定的上限进行比较;第五步,只有在亏损笔数低于上限时,才提交新的入场订单;第六步,一旦达到每周最大亏损笔数,立即平掉所有当前持有的仓位。让我们来详细分解这些步骤及其所需的代码。
步骤1.(可选)通过输入项设置每周最大亏损笔数
最便捷的方式是通过输入项来设置每周的最大亏损笔数,这样我们就不必为了调整这个参数而反复修改代码。方法如下:
// 通过输入项配置最大亏损交易数
maxLosses = input.int(10, title="最大亏损交易数(每周)",
minval=1)
这段代码使用 input.int() 函数创建了一个整数输入框。我们将其默认值设为10,并通过 minval=1 确保其最小值为1,这有助于简化后续代码。
我们将这个输入值存入 maxLosses 变量。(如果你不需要输入项并跳过此步,请注意,在后续代码中,maxLosses 指的是每周允许的最大亏损笔数。)
步骤2. 获取上周末时的累计亏损交易总数
要追踪一周内的亏损笔数,方法有多种。一个简单可靠的方法是利用 strategy.losstrades 变量,它返回策略自开始以来的累计亏损交易总数。
但我们的目标是计算本周的亏损,而非全部。因此,我们需要先获取上周末时 strategy.losstrades 的值作为基准。然后,在本周内,用当前的 strategy.losstrades 值减去这个基准值,即可得到本周新增的亏损笔数。以下是如何获取上周末的这个基准值:
// 获取策略在上周末时的累计亏损总数
prevLoseTrades = 0
prevLoseTrades := if dayofweek == dayofweek.monday and dayofweek != dayofweek[1]
strategy.losstrades[1]
else
nz(prevLoseTrades[1])
这里,我们首先声明了一个持久变量 prevLoseTrades。然后,一个 if/else 结构赋予了它实际的逻辑。if 条件包含两部分:当前K线的星期(dayofweek)是星期一(dayofweek.monday),并且当前K线是本日的第一根K线。当这两个条件同时成立时,意味着我们正处于本周的第一个交易时刻。此时,上周末的累计亏损数就是前一根K线收盘时的 strategy.losstrades[1] 值,我们将其存入 prevLoseTrades。在本周的其他时间,else 部分执行,让 prevLoseTrades 保持其前一根K线的值不变。我们使用 nz() 函数来处理脚本开始时可能出现的无历史值的情况。
关于此代码有两点说明:首先,如果你的交易周从周日开始,请将 dayofweek.monday 替换为 dayofweek.sunday。其次,这段代码在每周开始时获取亏损数,这实际上也重置了周度亏损计数,使得策略在新的一周可以重新开始交易。
步骤3. 确定本周的总亏损笔数
知道了上周末的累计亏损数后,我们便可以计算本周发生了多少笔亏损交易:
// 确定本周的亏损交易笔数
weeklyLosses = strategy.losstrades - prevLoseTrades
这里,我们用当前的累计亏损总数(strategy.losstrades)减去上周末的基准值(prevLoseTrades),并将结果存入 weeklyLosses 变量。
步骤4. 检查周度亏损笔数是否低于上限
有了本周的亏损笔数,我们就可以检查它是否低于我们之前设定的上限:
// 检查亏损笔数是否仍在上限之内
okToTrade = weeklyLosses < maxLosses
这里,我们判断 weeklyLosses 是否小于 maxLosses。如果是,布尔变量 okToTrade 就为 true;否则为 false。我们将这个判断结果存入变量,是为了让最后两步的代码更清晰易读。
步骤5. 仅在未达到周度亏损上限时才提交入场订单
现在我们有了一个 okToTrade 变量,它告诉我们策略是应该继续交易(true)还是暂停(false)。我们可以将它整合到策略的入场条件中:
// 仅在未达到最大亏损上限时才提交订单
if okToTrade and enterLong
strategy.entry("EL", strategy.long)
if okToTrade and enterShort
strategy.entry("ES", strategy.short)
这两个 if 语句只有在 okToTrade 为 true 的前提下,才会根据策略原有的 enterLong 或 enterShort 信号来建立新的仓位。一旦周度亏损笔数达到上限,okToTrade 将变为 false,从而阻止策略发送任何新的入场订单。
请注意,上述代码中的 enterLong 和 enterShort 仅为占位符;你需要将 okToTrade 整合到你自己策略的实际入场条件中。核心思想不变:在任何 strategy.entry() 或 strategy.order() 函数执行开仓操作前,都必须先检查 okToTrade 的状态。
步骤6. 达到周度亏损上限时平掉所有持仓
最后一步,我们让策略在达到周度亏损笔数上限时,平掉所有当前持仓,从而完全停止交易。
// 当周度亏损交易达到上限时,平掉所有仓位
if not okToTrade
strategy.close_all()
这个 if 语句使用 not 逻辑运算符来判断 okToTrade 是否为 false。如果是,就意味着策略已触及周度亏损笔数的上限。此时,我们调用 strategy.close_all() 函数,它会通过一笔市价单来平掉所有当前持仓。
需要注意的是,strategy.close_all() 生成的这笔平仓交易,如果运气不佳,其本身也可能是一笔亏损交易。在这种情况下,最终记录的周度亏损笔数会比我们设定的上限多1。
示例策略:在一周内发生n笔亏损后停止交易
下方的完整策略脚本整合了以上所有步骤。该策略基于两条指数移动平均线(EMA)的交叉进行交易。但这个交易过程不会无限持续,因为我们同时也在追踪每周的亏损交易笔数。一旦一周内发生10笔亏损,脚本就会平掉当前持仓,并暂停交易直到下周。策略的完整代码如下:
//@version=5
strategy(title="限制周度亏损交易",
overlay=false, precision=0)
// 步骤1:
// 通过输入项配置最大亏损交易数
maxLosses = input.int(10, title="最大亏损交易数(每周)",
minval=1)
// 计算移动平均线
fastMA = ta.ema(close, 5)
slowMA = ta.ema(close, 25)
// 定义交易条件
enterLong = ta.crossover(fastMA, slowMA)
enterShort = ta.crossunder(fastMA, slowMA)
// 步骤2:
// 获取策略在上周末时的累计亏损总数
prevLoseTrades = 0
prevLoseTrades := if dayofweek == dayofweek.monday and dayofweek != dayofweek[1]
strategy.losstrades[1]
else
nz(prevLoseTrades[1])
// 步骤3:
// 确定本周的亏损交易笔数
weeklyLosses = strategy.losstrades - prevLoseTrades
// 绘制周度亏损交易笔数及其上限
plot(weeklyLosses, style=plot.style_area, linewidth=4,
color=color.new(color.red, 85))
hline(0, color=color.gray, linestyle=hline.style_solid)
hline(maxLosses, color=color.maroon, linestyle=hline.style_solid,
linewidth=2)
// 步骤4:
// 检查亏损笔数是否仍在上限之内
okToTrade = weeklyLosses < maxLosses
// 步骤5:
// 仅在未达到最大亏损上限时才提交订单
if okToTrade and enterLong
strategy.entry("EL", strategy.long)
if okToToTrade and enterShort
strategy.entry("ES", strategy.short)
// 步骤6:
// 当周度亏损交易
// 达到上限时平掉所有仓位
if not okToTrade
strategy.close_all()
让我们看看这个策略的表现。在下方的EUR/GBP图表上,第一笔多头交易盈利平仓,因此没有增加周度亏损计数。但此后策略表现不佳,接连遭遇亏损,很快便触及了周度亏损上限。此时,策略平掉了持仓并暂停了本周的交易:
强制平仓的最后一笔订单也可能是亏损的。在这种情况下,周度亏损会比我们预设的上限多1。下方的比特币(BTC/USD)图表就是这样一个例子。一旦周度亏损达到上限,策略发送了平仓单。该订单在一个不利的价格退出了空头头寸,因此策略最终以11笔亏损结束了那一周:
其他在周度层面管理TradingView风险的方法,还包括对策略的周度亏损金额设置上限。要了解更多限制策略风险的方法,请参阅我们的风险管理分类文章。
编写一个输入选项,让TradingView策略只做多、只做空或双向交易
有很多方法可以限制交易风险。其中一种有效的方式是,在某些市场条件下,完全禁止策略进行多头或空头交易。例如,当市场明显处于强势上涨趋势时,我们可以通过禁止做空来避免不必要的损失。本文将向你展示如何编写一个自定义的设置,让一个TradingView策略可以灵活地选择只做多、只做空或双向交易。
轻松设定TradingView策略的交易方向
并非策略承担的每一次风险都能换来应有的回报。有些风险甚至更有可能导致额外的亏损。因此,在承担风险和保持谨慎之间找到一个平衡点,对提升策略的整体表现至关重要。
策略交易方向错误便是一个常见的风险,例如在明显的上升趋势中做空,或在剧烈下跌时盲目买入。要将策略的交易方向限制为只做多或只做空,我们可以使用TradingView内置的 strategy.risk.allow_entry_in() 函数。
然而,这个函数无法通过策略设置界面手动配置,它是一个纯代码功能。这有时会带来不便,尤其是因为每次修改并保存策略代码后,所有的输入选项都会被重置。但我们可以自己动手创建一个输入选项!这样,我们就能方便地在只做多、只做空和双向交易之间自由切换了。
实现这样一个输入选项需要三个步骤:第一步,使用 input.string() 函数创建一个输入项,并通过 options 参数为其提供”Long”、”Short”和”Both”三个选项,形成一个下拉菜单;第二步,创建两个布尔型(真/假)变量,分别对应多头和空头方向,这两个变量将根据输入项的当前值来判断是否允许在该方向上交易;第三步,在提交多头和空头开仓订单的逻辑中,使用这两个布尔变量作为额外的过滤条件。让我们逐步分解每一步所需的代码。
步骤1. 创建一个带下拉菜单的输入项
首先,我们创建一个带下拉菜单的输入项,以便用户可以方便地配置策略的交易方向。我们通过调用 input.string() 函数来实现:
// 创建一个输入选项来配置交易方向
tradeDirection = input.string("Both", title="交易方向",
options=["Long", "Short", "Both"])
这里,我们创建了一个名为交易方向的字符串输入项,其默认值为”Both”。通过 options 参数,我们为其定义了三个可选值:”Long”、”Short”和”Both”。这三个选项将以一个方便的下拉菜单形式呈现在策略的设置面板中。我们将输入项的当前值保存在 tradeDirection 变量中,供后续使用。
步骤2. 将输入值转换为布尔交易条件
接下来,我们需要将用户的选择(一个字符串)转换成我们的开仓逻辑可以理解的条件。最简单的方法是创建两个布尔变量,稍后将它们作为开仓的通行证。
// 将输入选项的值转换为交易条件
longOK = tradeDirection == "Long" or tradeDirection == "Both"
shortOK = tradeDirection == "Short" or tradeDirection == "Both"
longOK 变量用于判断是否允许做多。只有当用户在下拉菜单中选择了”Long”或”Both”时,longOK 的值才为 true。同理,shortOK 变量在用户选择了”Short”或”Both”时为 true,表示允许做空。
步骤3. 在开仓逻辑中使用布尔变量
最后一步,我们在提交开仓订单时,使用上一步创建的布尔变量作为条件之一:
// 提交开仓订单
if longOK and enterLong
strategy.entry("EL", strategy.long, qty=1)
if shortOK and enterShort
strategy.entry("ES", strategy.short, qty=1)
在第一个 if 语句中,我们要求 longOK 和 enterLong(一个代表原始多头信号的示例变量)必须同时为 true 时,才执行 strategy.entry() 函数来开立多仓。
同样,第二个 if 语句要求 shortOK 和 enterShort 必须同时为 true,才会开立空仓。
由于我们在提交任何开仓订单之前都检查了 longOK 和 shortOK,因此策略只能在我们通过交易方向输入项所配置的方向上进行交易。
重要提示:你需要在你自己的策略中,找到所有用于开仓的函数(包括 strategy.entry() 和 strategy.order()),并在其条件判断中加入 longOK 或 shortOK 的检查。
另一个重要提示:如果你的代码依赖 strategy.entry() 来实现仓位的反转(例如,从多头直接开仓变为空头),那么你需要编写额外的平仓逻辑。原因是,当交易方向输入项只允许多头交易时,一个试图做空的 strategy.entry() 指令将被完全忽略,它不会像使用 strategy.risk.allow_entry_in() 那样自动平掉多头仓位。因此,你必须使用 strategy.exit()、strategy.order() 或 strategy.close() / strategy.close_all() 等函数来明确地平掉多头仓位。
示例策略:通过输入项控制多空交易
下方的完整策略脚本融合了以上所有步骤。该策略基于两条简单移动平均线(SMA)的交叉进行交易。但只有在交易方向输入项允许的情况下,才会执行相应的交易信号。策略的完整代码如下:
//@version=5
strategy(title="示例:多头或空头的输入", overlay=true)
// 步骤 1: 创建交易方向的输入选项
tradeDirection = input.string("Both", title="交易方向",
options=["Long", "Short", "Both"])
// 步骤 2: 将输入值转换为布尔交易条件
longOK = tradeDirection == "Long" or tradeDirection == "Both"
shortOK = tradeDirection == "Short" or tradeDirection == "Both"
// 计算并绘制移动平均线
quickMA = ta.sma(close, 10)
slowMA = ta.sma(close, 30)
plot(quickMA, color=color.orange)
plot(slowMA, color=color.teal)
// 定义原始的多空交易信号
enterLong = ta.crossover(quickMA, slowMA)
enterShort = ta.crossunder(quickMA, slowMA)
// 步骤 3: 提交开仓订单(带方向过滤)
if longOK and enterLong
strategy.entry("EL", strategy.long, qty=1)
if shortOK and enterShort
strategy.entry("ES", strategy.short, qty=1)
// 提交平仓订单
if strategy.position_size > 0
strategy.exit("XL", stop=slowMA)
if strategy.position_size < 0
strategy.exit("XS", stop=slowMA)
这个策略创建的输入选项在设置面板中看起来是这样的:
当输入项设为”Both”时,策略会对BTC进行双向交易:
当我们将选项切换为”Long”时,策略便只执行多头交易:
而当输入项设为”Short”时,图表上便只剩下BTC/USD的空头交易:
补充说明一点,创建一个交易方向的输入选项不仅让策略更易于配置,它还有一个重要的优势。当我们使用内置的 strategy.risk.allow_entry_in() 函数来限制交易方向时,这个限制对 strategy.order() 函数是无效的。这意味着,即使我们禁止了做空,策略仍有可能通过 strategy.order() 建立空头仓位!幸运的是,通过本文介绍的自定义代码方法,我们可以确保包括 strategy.order() 在内的所有订单函数都严格遵守我们设定的交易方向。
要了解更多管理和限制策略风险的方法,可以参阅TradingView风险管理的相关主题分类。








