策略风险管理功能概述
TradingView的Pine脚本语言内置了数个用于管理交易风险的函数。在本文中,我们将深入探讨这些函数,并学习如何在我们的TradingView策略中实际应用它们。
几乎没有哪个交易策略能在所有市场环境中都保持盈利。为了让我们的策略能在有利的市场条件下充分获利,就必须防止它在逆境中损失过大。实现这一目标的有效途径之一就是实施良好的风险管理。
TradingView为几种关键的风险指标提供了内置支持。我们可以将这些风险管理函数划分为两大类。第一类是全局性风险函数:strategy.risk.allow_entry_in() 使策略只能单向交易(只做多或只做空);strategy.risk.max_cons_loss_days() 限制策略可承受的最大连续亏损天数;strategy.risk.max_drawdown() 为策略的总资金回撤设定一个上限;strategy.risk.max_position_size() 限制策略的最大持仓规模。第二类是日内风险函数:strategy.risk.max_intraday_filled_orders() 限制单个交易日内的成交订单数量;strategy.risk.max_intraday_loss() 为策略设定单日最大可承受的亏损上限。
请注意,目前所有这些风险规则都无法通过界面上的选项进行手动设置。因此,如果我们的策略需要应用其中的一项或多项规则,就必须在代码中亲手编写它们。接下来,让我们逐一详细了解这些风险函数。
全局性风险管理函数
第一类是全局性风险函数。一旦这类规则被触发,它们将在整个回测期间以及所有后续的实盘信号中彻底停止该策略的运作。相比之下,日内风险规则只会暂停策略当天的交易。
第一个全局风险函数是 strategy.risk.allow_entry_in()。它的作用是让我们的策略只能沿着单一方向进行交易,即只做多或只做空。该函数仅有一个参数:value。通过它,我们可以指定策略的交易方向是做多(strategy.direction.long)还是做空(strategy.direction.short)。
例如,要让策略只能执行空头交易,我们可以在脚本中加入如下代码:
// 只允许建立空头仓位
strategy.risk.allow_entry_in(value=strategy.direction.short)
如果策略只应执行多头交易,则应这样使用该函数:
// 只允许提交多头开仓订单
strategy.risk.allow_entry_in(value=strategy.direction.long)
另一个全局风险函数是 strategy.risk.max_cons_loss_days()。它用于创建一个关于最大连续亏损天数的风控规则。该函数只有一个参数:count,用于设定最大连续亏损天数的阈值。一旦策略的连续亏损天数达到这个数值,TradingView便会停止该策略。例如,要让策略在连续亏损5天后自动停止交易,我们可以这样写:
// 在策略资金连续下降5天后停止交易
strategy.risk.max_cons_loss_days(count=5)
另一种限制风险的有效方法是使用 strategy.risk.max_drawdown() 函数。该函数可以指定一个最大回撤容忍度,一旦策略的实际回撤触及此限,TradingView便会停止策略。
我们通过两个参数来配置此函数。第一个是 value,用于设定最大回撤的具体数值。第二个是 type,用于定义回撤的计算方式。回撤有两种衡量方式:使用 strategy.cash,最大回撤将以图表交易品种的货币金额来表示;而使用 strategy.percent_of_equity,回撤则以策略权益的百分比来计算。
因此,如果我们不希望策略亏损超过5000个货币单位,可以这样设置:
// 亏损额(以图表品种的货币计)不应超过5000
strategy.risk.max_drawdown(value=5000, type=strategy.cash)
若要将最大回撤控制在策略总权益的10%以内,则应这样调用函数:
// 在权益亏损达到10%后停止交易
strategy.risk.max_drawdown(value=10, type=strategy.percent_of_equity)
交易头寸过大同样是重大的风险来源。为了降低此类风险,我们可以使用 strategy.risk.max_position_size() 函数。该函数可以将策略的任何多头或空头仓位限制在一个最大数量内(如合约数、股数、单位或手数)。该函数只有一个参数:contracts。我们赋予此参数的数值,即是我们愿意持有的最大多头或空头仓位规模。例如,要将策略的最大仓位限制在15手以内,可以这样写:
// 任何持仓规模都不得超过15个合约、股、单位或手数
strategy.risk.max_position_size(contracts=15)
日内交易风险函数
TradingView提供的另一类风险函数,主要用于限制日内交易的风险。当这类规则被触发时,TradingView只会暂停策略在当天的交易活动。到下一个交易日,策略便可以重新开始运作。
为了防止策略在一天内过于频繁地交易,我们可以使用 strategy.risk.max_intraday_filled_orders() 函数。当一天内成交的订单数量达到该函数设定的上限时,TradingView便会暂停策略当天的后续交易。配置此函数只需一个参数:count。该参数的值代表了单个交易日内,所有买入、卖出、做空和回补订单被成交的总次数上限。例如,要将日内成交订单数限制在20笔以内,可以这样设置:
// 在单个日内交易时段,成交的订单数不得超过20笔
strategy.risk.max_intraday_filled_orders(count=20)
另一个日内风险函数是 strategy.risk.max_intraday_loss()。该函数允许我们指定单日内所能承受的最大亏损额。一旦实际亏损达到这个数值,TradingView便会暂停策略当天的交易。
我们同样通过两个参数来配置此函数。第一个是 value,定义了最大日内亏损的具体数值。另一个是 type,指定了亏损的衡量方式。亏损的衡量方式也有两种:使用 strategy.cash,日内亏损将以图表交易品种的货币金额来计算;而使用 strategy.percent_of_equity,日内亏损则以策略权益的百分比来计算。
因此,若要将单日亏损控制在450个货币单位以内,可以这样写:
// 在单个日内交易时段,亏损额不得超过450个货币单位
strategy.risk.max_intraday_loss(value=450, type=strategy.cash)
若要将单日最大风险控制在策略总权益的3%以内,则应这样执行:
// 在单个交易时段,风险敞口最多为策略权益的3%
strategy.risk.max_intraday_loss(value=3, type=strategy.percent_of_equity)
顺带一提,除了本文讨论的内置风险函数,我们还可以编写自己的风险管理代码,实现更贴合自身交易习惯的风控逻辑。
简单总结一下:任何交易策略都有表现不佳的时候。为了防止在困难的市场环境中损失过多的资金,我们可以运用多种内置的风险管理函数。第一类是全局性风险规则,它们会对策略产生永久性影响,包括限制交易方向的 strategy.risk.allow_entry_in()、限制连续亏损天数的 strategy.risk.max_cons_loss_days()、限制最大资金回撤的 strategy.risk.max_drawdown(),以及限制最大持仓规模的 strategy.risk.max_position_size()。另一类是日内风险函数,它们的影响仅限于单个交易日,包括限制日内成交订单数量的 strategy.risk.max_intraday_filled_orders(),和为日内亏损设定上限的 strategy.risk.max_intraday_loss()。
同一策略中的多个风险管理规则
TradingView提供了一系列用于管理策略风险的内置函数。有时,仅用一个函数就足以改善我们脚本的表现;但在其他情况下,组合使用多个风险管理函数能更有效地限制策略风险。本文将探讨如何将不同的风险规则结合在一起。
组合风险管理函数的核心原则
虽然在本系列的其他文章中,我们都是独立地讨论每一个风险管理函数,但这并非是使用它们的唯一方式。我们完全可以在同一个策略中,组合应用多个风险管理函数。
当我们这样做时,一个核心原则是:设置最严格的风险管理函数会最先被触发。在某种意义上,各项风险管理规则就像在相互竞赛,看谁最先达到触发条件。这也可以理解为,当同一个脚本中存在多个风险管理函数时,TradingView会对每一个进行独立评估。
因此,如果我们同时限制了策略的最大持仓规模和日内交易次数,那么只要其中任意一条规则被触发,就足以让策略停止提交新的交易。
但是,TradingView不会将多个风险规则合并成一个复合规则。例如,当我们同时限制了最大回撤和连续亏损天数,TradingView并不会等到回撤和连亏天数这两个条件都满足后才停止策略。恰恰相反,只要其中任意一个条件被满足,就足以让策略停止运行。
这里要特别注意:我们无法将TradingView的内置风险管理函数逻辑上连接起来。因此,像”当策略连续亏损7天并且资金回撤达到或超过$2,500时,才停止交易”这样的复合规则是无法直接实现的。实际情况是,我们的策略要么在连续亏损7天后停止,要么在回撤达到$2,500时停止。如果你希望实现这种”与”逻辑的复合条件,就必须为此编写自定义的代码逻辑。
所有风险管理函数概览
在组合使用之前,我们先快速了解一下有哪些可用的风险管理函数及其用途:strategy.risk.allow_entry_in() 限制策略只进行多头或空头交易;strategy.risk.max_cons_loss_days() 在连续亏损达到指定天数后停止策略;strategy.risk.max_drawdown() 当达到设定的最大回撤值时暂停所有交易;strategy.risk.max_position_size() 防止策略建立过大的仓位;strategy.risk.max_intraday_filled_orders() 限制日内已成交的订单总数;strategy.risk.max_intraday_loss() 当达到设定的日内亏损上限后停止当日交易。
现在,让我们看看组合使用这些函数会发生什么。
在同一策略中应用多条风险规则
当我们在同一个策略中放置多个风险管理函数时,TradingView会对每一条规则进行独立于其他的评估。那条最先被触发的风险规则,将决定策略后续的响应行为——这通常意味着停止交易。
示例一:同时限制连续亏损天数与最大回撤。一种常见的组合方式是,既要限制策略的连续亏损天数,又要防止最大回撤本身变得过大。对于前者,我们使用 strategy.risk.max_cons_loss_days() 函数,它会在策略权益连续多日下滑后停止交易。对于后者,我们使用 strategy.risk.max_drawdown() 函数来设置一个回撤的熔断阈值。一旦实际回撤达到或超过该值,TradingView便会停止策略。将这两个函数结合起来,是一种非常稳健的风险控制方法。在脚本中,这两个函数可以这样组合使用:
//@version=5
strategy(title="限制策略风险:连亏与回撤")
// ...
// 在连续3个亏损日后停止交易
strategy.risk.max_cons_loss_days(count=3)
// 或者,在回撤达到10,000时停止交易
strategy.risk.max_drawdown(value=10000, type=strategy.cash)
// ...
这里,我们首先调用 strategy.risk.max_cons_loss_days(),并将其 count 参数设为3,允许策略最多连续亏损3天。但如果在第3天结束时资金曲线未能回升,TradingView便会平掉当前持仓并停止后续交易。
同时,我们也执行了 strategy.risk.max_drawdown()。我们将 value 参数设为10000,type 参数设为 strategy.cash,这意味着我们以账户现金来计算回撤,并允许最大回撤为10,000个货币单位。一旦达到这个值,TradingView同样会平仓并停止交易。
因此,这个策略有两种可能被提前终止的情况:要么是连续亏损3天,要么是回撤达到10,000。TradingView并不要求这两个条件同时发生。只要其中任意一条风险规则被触发,就足以停止交易。
示例二:同时限制持仓规模与日内订单数。另一种限制风险的思路是控制交易的活跃度。通过 strategy.risk.max_position_size() 函数,我们可以限制策略在单个多头或空头方向上所能持有的最大合约、股数或单位。而 strategy.risk.max_intraday_filled_orders() 函数则限制了策略每日可以成交的入场和出场订单总数。以下是同时使用这两个风险函数的方法:
//@version=5
strategy(title="示例:限制交易活跃度与规模")
// ...
// 单方向持仓不超过10个合约
strategy.risk.max_position_size(contracts=10)
// 并且,每日交易不超过3次
strategy.risk.max_intraday_filled_orders(count=3)
// ...
这里,我们首先执行 strategy.risk.max_position_size(),将 contracts 参数设为10,允许在单方向上最多持有10个单位的仓位。一旦达到这个持仓规模,TradingView将阻止 strategy.entry() 函数建立新的仓位。
同时,我们也执行 strategy.risk.max_intraday_filled_orders(),将 count 参数设为3,允许每日最多成交3笔订单(包含入场和出场)。一旦达到这个数量,TradingView便会停止当天的交易。
由于这两条规则相互独立,我们的策略有两种可能停止交易的情况:要么是达到了最大持仓规模,要么是达到了日内交易次数上限。
在同一脚本中多次使用相同的风险函数
还有一种选择是在同一个脚本中多次使用同一个风险管理函数。通过这种方式,我们可以用不同的维度来衡量同一个风险指标(例如,最大回撤)。当我们多次使用同一个风险函数时,其背后的逻辑与使用不同风险函数时完全相同:最严格的规则最先被应用,且TradingView对每条规则都进行独立评估。
不过,并非每个风险函数重复执行都有意义。例如,如果我们两次限制最大持仓规模,那么设置数值最小的那个总是会生效,这使得另一个 strategy.risk.max_position_size() 调用变得多余。
以下风险管理函数在被执行两次时,能提供额外的功能:通过 strategy.risk.max_drawdown(),我们可以同时以固定金额和权益百分比来定义策略的最大回撤;通过 strategy.risk.max_intraday_loss(),我们也可以在执行两次时,同时以固定金额和权益百分比来定义策略允许的日内亏损。
但以下这些风险函数在重复使用时则没有额外的好处,反而可能造成代码冗余:strategy.risk.allow_entry_in(),如果我们用两次来分别允许做多和做空,这其实就恢复了默认行为,没有意义;strategy.risk.max_cons_loss_days(),当多次使用时,天数设置最小的那个会决定策略的行为,其他的调用都是多余的;strategy.risk.max_position_size(),最小的持仓规模设置会主导策略行为;strategy.risk.max_intraday_filled_orders(),最小的交易次数设置会主导策略行为,其他的实例则变得没有必要。
顺带一提,多次使用这些风险函数并不会导致编程错误。但是,为了代码的清晰易懂,我们最好确保每种函数只调用一次(除非有明确的复合度量需求)。
让我们看看如何两次使用同一个风险管理函数。通过 strategy.risk.max_drawdown(),我们可以用两种方式来衡量回撤:基于固定货币金额,或是基于权益的百分比。当我们两次使用它时,便可以同时从这两个维度来监控回撤。代码如下:
//@version=5
strategy(title="以金额和百分比设置最大回撤")
// ...
// 在亏损达到5,000个货币单位后停止交易
strategy.risk.max_drawdown(value=5000, type=strategy.cash)
// 或者,在权益回撤达到15%后停止,以先触发者为准
strategy.risk.max_drawdown(value=15, type=strategy.percent_of_equity)
// ...
第一个 strategy.risk.max_drawdown() 调用将最大回撤设置为5,000个货币单位(value=5000,type=strategy.cash)。第二个调用则将最大回撤限制为权益的15%(value=15,type=strategy.percent_of_equity)。
现在,这两条回撤限制规则中,任何一条最先被触发的,都将使策略停止。在某种意义上,这两条规则在相互竞赛,谁先触及红线,谁就获得停止策略的权力。
简单总结一下:TradingView的风险管理函数能帮助我们减少策略所承担的不必要风险。当我们在同一个策略中组合使用它们时,便可以更精确地定义我们的最大风险容忍度。当脚本中存在多个风险管理函数时,TradingView会对每一条进行独立评估,因此任意一条被触发就足以停止交易,最先被触发的那条规则将决定策略的响应行为。我们也可以在同一个脚本中多次使用同一个风险管理函数,从而以不同维度衡量同一项风险——但只有 strategy.risk.max_drawdown() 和 strategy.risk.max_intraday_loss() 这样支持金额与百分比两种度量的函数,重复使用才有实际意义。


