使用TradingView的VIX反转策略交易标准普尔500指数
反转交易策略试图捕捉市场的每一次上下波动,但其缺点是始终在市,承担着持续的风险。本文将介绍一种旨在降低这种市场风险的策略思想——VIX反转策略。让我们来一探究竟。
微趋势交易:托马斯·卡尔的VIX反转系统
在其著作《每日收入的微趋势交易》(Micro-Trend Trading for Daily Income)中,托马斯·卡尔(Thomas Carr)分享了多种交易思想。他的所有策略都围绕着微趋势交易这一核心概念。
微趋势交易是一种日内交易方法,它使用极短的时间框架(如1、3或5分钟图),并在市场波动剧烈时表现良好。其核心理念是,只要市场提供利润,就及时获利了结,并尽可能避免持仓隔夜。
这些微趋势交易策略之一,便是VIX反转系统。该系统基于标普500指数与其波动率指数——芝加哥期权交易所VIX指数之间的负相关关系来建立多头和空头仓位。通常,当市场参与者变得紧张时,波动性(VIX)会上升,而标普500指数则倾向于下跌。反之,当市场相对平静时,波动性下降,股价则有更大的上涨潜力。
为了从这种行为中获利,VIX反转策略的交易方向与标普500指数的预期走向相反。也就是说,当VIX反转向下时,策略做多标普500指数。而当VIX反转向上时,策略则做空标普500指数的追踪ETF,或买入反向ETF。接下来,让我们仔细看看该策略的交易规则。
VIX反转策略的交易规则
做多入场:当VIX的5周期简单移动平均线(SMA)下穿其15周期SMA时,做多标普500指数。如果做多信号出现时策略正持有空仓,则将该空头仓位反转为多头仓位。做多离场:在交易日结束收盘时,退出多头头寸。做空入场:当VIX的5周期SMA上穿其15周期SMA时,做空标普500指数。如果做空信号出现时策略正持有多仓,则将该多头仓位反转为空头仓位。做空离场:在交易日结束时,平掉所有已有的空头仓位。
VIX反转策略是为5分钟图设计的。它通常交易一个标普500指数的追踪ETF(或与之高度相关的品种)。然而,该ETF应具备高成交量和全天相对平滑的价格走势。
与大多数其他交易策略相比,VIX反转策略有几个不寻常的特点。首先,该策略在日内始终在市,要么做多,要么做空,只有在交易日结束时才会平仓。另一个非典型特征是,该策略不使用止损,而是简单地在移动平均线反向交叉(或交易日结束)时平掉仓位。
为TradingView Pine编写VIX反转策略
现在,让我们根据上述交易规则,创建一个完整的TradingView策略脚本。使用模板是编写策略的一个有效方法,它能为我们提供一个框架,并将编程任务分解成更小、更易于管理的部分。
对于VIX反转策略,我们将使用以下模板:
//@version=5
// 步骤1. 定义策略设置
// 步骤2. 计算策略所需指标值
// 步骤3. 在图表上输出数据
// 步骤4. 定义多头交易条件
// 步骤5. 定义空头交易条件
// 步骤6. 提交入场订单
// 步骤7. 发送出场订单
如果你想跟随下方的代码讨论,请在TradingView的Pine编辑器中创建一个新的策略脚本,并将上述模板粘贴进去。(如果你只想看完整的策略代码,请跳转至本文末尾。)
为了让你对我们即将编写的代码有个直观的认识,以下是最终完成的VIX反转策略在图表上的样子:
现在,就让我们开始动手,编写这个VIX反转策略的脚本。
步骤1:定义策略设置和输入选项
首先,我们必须定义策略的基础设置。为此,我们调用 strategy() 函数:
// 步骤1. 定义策略设置
strategy(title="VIX反转策略", overlay=true,
pyramiding=0, initial_capital=100000,
scale=scale.left,
default_qty_type=strategy.fixed,
default_qty_value=100,
commission_type=strategy.commission.cash_per_order,
commission_value=8, slippage=2)
我们通过 title 参数为策略命名。overlay=true 使策略直接叠加在主图表上。我们根据交易规则禁止了金字塔式加仓(pyramiding=0),并设定初始资金为100,000(initial_capital=100000)。默认的订单数量(default_qty_type)设为固定手数(strategy.fixed),每笔交易固定为100股(default_qty_value=100)。在交易成本方面,我们设定了相对保守的佣金和滑点,以避免高估策略表现。
接下来,我们为策略创建一些可配置的输入选项。前两个用于设置移动平均线:
// 移动平均线的输入选项
fastMALen = input.int(5, title="快线SMA周期")
slowMALen = input.int(15, title="慢线SMA周期")
我们创建了两个整数输入项,分别用于设置快、慢两条SMA的计算周期,默认值分别为5和15。
然后,我们创建两个输入项来定义脚本的交易时段:
// 交易时段输入选项
sessRange = input.session("0930-1550", title="交易时段范围")
showSessEnd = input.bool(true, title="显示时段结束?")
由于VIX反转策略需要在交易日结束前平仓,我们需要告诉脚本交易何时结束。为此,我们使用 input.session() 创建一个名为交易时段范围的输入项,默认值为”0930-1550″。这给了策略大约10分钟的时间在SPY(标普500 ETF)于16:00收盘前退出交易。另一个布尔型输入项则用于控制是否在图表上高亮显示非交易时段的背景。
步骤2:计算交易策略所需的值
在第二步,我们计算策略所需的数据,包括:加载VIX数据,计算移动平均线,以及判断当前K线是否处于交易时段内。
首先,我们抓取VIX波动率指数的数据:
// 步骤2. 计算策略所需值
// 获取VIX数据
vixData = request.security("CBOE:VIX", timeframe.period, close)
我们使用 request.security() 函数,从芝加哥期权交易所(CBOE)获取VIX指数在当前图表时间周期下的收盘价数据,并存入 vixData 变量。
有了VIX数据,我们就可以计算它的移动平均线:
// 计算移动平均线
fastSMA = ta.sma(vixData, fastMALen)
slowSMA = ta.sma(vixData, slowMALen)
我们调用 ta.sma() 函数,基于获取到的 vixData 和用户设定的周期,分别计算出快、慢两条SMA均线。
接下来,我们判断当前价格K线是否处于我们设定的交易时段内:
// 确定价格K线是否在时段内
inSession = not na(time(timeframe.period, sessRange))
这段代码虽然只有一行,但逻辑略微复杂。我们一步步解析:time(timeframe.period, sessRange) 会判断当前K线是否在我们设定的”0930-1550″时段内,如果在,则返回K线的时间戳,否则返回 na(非数值)。na() 函数会判断其参数是否为 na,如果是则返回 true。因此,na(time(...)) 在K线位于时段外时为 true。最后,我们使用 not 运算符将其反转,从而让 inSession 变量在K线位于时段内时为 true,反之为 false。
步骤3:输出数据并可视化信号
在第三步,我们在图表上输出策略所需的值,以便验证和跟踪脚本的决策。首先,我们绘制VIX的移动平均线:
// 步骤3. 输出策略数据
plot(fastSMA, color=color.teal, title="VIX快线SMA")
plot(slowSMA, color=color.orange, title="VIX慢线SMA")
然后,为了方便观察,我们用背景色高亮显示非交易时段:
// 高亮显示时段结束
bgcolor(showSessEnd and not inSession ? color.new(color.teal, 80) : na)
只有当用户勾选了”显示时段结束?”并且当前K线不在交易时段内时(not inSession 为 true),背景才会被染成带透明度的青色。
步骤4:编写多头交易规则
第四步,我们将策略的多头交易规则转化为Pine Script代码:
// 步骤4. 定义多头交易条件
enterLong = ta.crossunder(fastSMA, slowSMA) and
inSession
exitLong = strategy.position_size > 0 and
not inSession
enterLong 变量在两个条件同时满足时为 true:一是VIX的快线SMA下穿慢线SMA;二是该交叉信号必须发生在我们的交易时段内(inSession)。
exitLong 变量则定义了多头平仓的条件:一是策略当前持有多仓(strategy.position_size > 0);二是当前K线已处于交易时段之外(not inSession)。
步骤5:编写空头交易条件
接下来,我们将策略的空头规则转化为代码:
// 步骤5. 定义空头交易条件
enterShort = ta.crossover(fastSMA, slowMA) and
inSession
exitShort = strategy.position_size < 0 and
not inSession
空头交易的逻辑与多头完全对称。enterShort 在VIX的快线SMA上穿慢线SMA,且处于交易时段内时触发。exitShort 则在持有空仓且交易时段结束后触发。
步骤6:提交开仓订单
在策略代码的下一部分,我们终于可以提交订单了:
// 步骤6. 提交开仓订单
if enterLong
strategy.entry("EL", strategy.long)
if enterShort
strategy.entry("ES", strategy.short)
当 enterLong 为 true 时,我们调用 strategy.entry() 开立一个ID为”EL”的多头仓位。当 enterShort 为 true 时,则开立ID为”ES”的空头仓位。VIX反转策略要求我们在收到反向信号时反转仓位,幸运的是,strategy.entry() 函数会自动处理这一逻辑。
步骤7:提交平仓订单
最后,策略代码的最后一部分在交易时段结束时平掉仓位:
// 步骤7. 提交平仓订单
if exitLong
strategy.close("EL", comment="XL")
if exitShort
strategy.close("ES", comment="XS")
当 exitLong 为 true 时,我们调用 strategy.close() 来平掉ID为”EL”的多头仓位,以避免持仓过夜。空头平仓的逻辑与此类似。
VIX反转策略的表现
让我们先从一个积极的观察开始。当市场出现清晰的日内趋势时,VIX反转策略能够很好地捕捉到大部分波段并实现盈利。
例如,在下方的图表中,策略在早盘VIX均线死叉时做多,并由于交叉状态一直维持到尾盘,成功地在SPY上捕捉到了一波可观的日内上涨趋势:
然而不幸的是,VIX反转策略也有其持续亏损的时期。这部分是因为该策略是一个永远在市的系统,即便在许多不具备良好交易潜力的市场环境中也依然在交易。
例如,下方的图表显示了标普500 ETF在近两天的时间里都处于横盘整理状态。在此期间,尽管市场毫无趋势可言,VIX反转策略却仍在频繁交易:
策略的资金曲线图如下所示。这张图清晰地表明,VIX反转策略没有真正的优势(edge):脚本的资金曲线几乎处于持续的回撤之中。直到大约340笔交易之后,权益才回升了约$1,000,但此时策略早已亏损累累:
下方的表格展示了该策略的回测结果。虽然该策略最终亏损,但其中很大一部分损失是由交易成本造成的。当然,策略相当频繁的交易频率也对此功不可没。并且,尽管平均盈利大于平均亏损,但其胜率太低,不足以覆盖所有的亏损和成本。
值得注意的是,尽管回测包含了数百笔交易,但测试的时间周期实际上很短:大约只有3个月。这似乎是TradingView在图表上加载K线数量的一个限制。
| 表现指标 | 标普500指数ETF (SPY) | 纳斯达克100指数ETF (QQQ) |
|---|---|---|
| 首次交易 | 2018-08-06 12:50 | 2018-08-06 12:50 |
| 最后交易 | 2018-11-02 15:55 | 2018-11-02 15:50 |
| 时间框架 | 5分钟 | 5分钟 |
| 净利润 | -$3,959 | -$4,568 |
| 总利润 | $6,629 | $5,502 |
| 总亏损 | -$10,588 | -$10,070 |
| 最大回撤 | $5,180 | $5,463 |
| 盈利因子 | 0.626 | 0.546 |
| 总交易数 | 374 | 367 |
| 胜率 | 27.81% | 30.52% |
| 平均每笔交易 | -$10.59 | -$12.45 |
| 平均盈利交易 | $63.74 | $49.12 |
| 平均亏损交易 | -$39.21 | -$39.49 |
| 平均盈亏比 | 1.625 | 1.244 |
| 支付佣金 | $3,504 | $3,448 |
| 每笔订单滑点 | 2个跳动点 | 2个跳动点 |
改进思路与新策略方向
从上表可以看出,VIX反转策略无疑有很大的改进空间。以下是一些你可能会觉得有趣的、值得进一步探索的思路。
VIX反转策略在大部分时间里都持有仓位,但适合交易移动平均线交叉的有利市场条件并非常常出现。如果我们能过滤掉那些没有前景的交易机会,策略的表现很可能会得到显著改善。或许,更高时间框架的ADX或平均真实波幅(ATR)可以帮助我们过滤信号。
根据卡尔的说法,当VIX处于高位(高于20甚至30),并且被交易的品种本身日内波幅很大时,VIX反转策略表现更好。也许这类过滤器能让策略的表现更上一层楼。
VIX反转策略没有止损订单或其他限制亏损的机制。虽然设置止损可能会让我们错失一些后来转为盈利的交易,从而降低绝对收益,但经风险调整后的回报很可能会变得好得多。此外,知道市场中有一个止损在保护着我们的持仓,也能带来一些心理上的安宁。
该策略目前交易标普500指数,但只看VIX数据。虽然VIX波动率数据有助于改善标普500策略,但完全不包含任何来自被交易品种本身的信号,似乎有些奇怪。例如,当SPY自身的日内波幅已经达到其日均ATR时,那么来自VIX的信号很可能已没有多少后续空间了。
VIX反转策略试图通过不过夜持仓来限制风险,但这给策略带来了两个主要缺点:高昂的交易成本,以及错失隔夜的价格波动。正如阿图彻所指出的,很大一部分的价格回报是在市场休市的隔夜时段产生的。因此,如果VIX反转策略能捕捉多日的价格移动,其表现可能会更好。那样,策略不仅可以从对其有利的隔夜跳空中获利,还能减少交易成本。
我们也可以尝试通过减少其承担的风险来使策略变得更好。例如,限制日内的交易次数或限制策略的日内亏损。
完整代码:TradingView的VIX反转系统
以下是VIX反转策略的完整代码。关于代码的作用和更多细节,请参阅上文的讨论。
//@version=5
// 步骤1. 定义策略设置
strategy(title="VIX反转", overlay=true,
pyramiding=0, initial_capital=100000,
scale=scale.left,
default_qty_type=strategy.fixed,
default_qty_value=100,
commission_type=strategy.commission.cash_per_order,
commission_value=8, slippage=2)
// --- 输入项 ---
// 移动平均线周期
fastMALen = input.int(5, title="快线SMA周期")
slowMALen = input.int(15, title="慢线SMA周期")
// 交易时段设置
sessRange = input.session("0930-1550", title="交易时段范围")
showSessEnd = input.bool(true, title="显示非交易时段?")
// 步骤2. 计算策略所需指标值
// 获取VIX数据作为信号源
vixData = request.security("CBOE:VIX", timeframe.period, close)
// 计算VIX的移动平均线
fastSMA = ta.sma(vixData, fastMALen)
slowSMA = ta.sma(vixData, slowMALen)
// 判断当前K线是否处于交易时段内
inSession = not na(time(timeframe.period, sessRange))
// 步骤3. 在图表上输出数据
// 绘制VIX的均线
plot(fastSMA, color=color.teal, title="VIX快线SMA")
plot(slowSMA, color=color.orange, title="VIX慢线SMA")
// 以背景色高亮显示非交易时段
bgcolor(showSessEnd and not inSession ? color.new(color.teal, 80) : na)
// 步骤4. 定义多头交易条件
enterLong = ta.crossunder(fastSMA, slowSMA) and
inSession
// 离场条件:不在交易时段内(即收盘平仓)
exitLong = strategy.position_size > 0 and
not inSession
// 步骤5. 定义空头交易条件
enterShort = ta.crossover(fastSMA, slowSMA) and
inSession
// 离场条件:不在交易时段内(即收盘平仓)
exitShort = strategy.position_size < 0 and
not inSession
// 步骤6. 提交入场订单 (反转信号)
if enterLong
strategy.entry("EL", strategy.long)
if enterShort
strategy.entry("ES", strategy.short)
// 步骤7. 发送出场订单 (日内收盘平仓)
if exitLong
strategy.close("EL", comment="XL")
if exitShort
strategy.close("ES", comment="XS")
为TradingView编写的宽幅周中反转策略
当交易周开局强劲时,交易者会变得兴奋,并顺着价格动能的方向进行布局。但随着时间的推移,进一步买入(或卖出)的兴趣会逐渐枯竭。那如果我们在周初就试图反向交易这股动能呢?本文将探讨一个正是基于此理念的策略思想。
詹姆斯·阿图彻的宽幅周中反转策略
在其著作《像对冲基金一样交易》(Trade Like a Hedge Fund)中,詹姆斯·阿图彻(James Altucher)分享了多种交易思想。其中一些策略看起来很简单,但他坚信,基础的策略效果最好,也更具稳健性。此外,他从不孤立地交易任何单一策略,而是认为获得良好且持续回报的最佳方式,是交易一个由交易着不相关品种的、不相关的策略所组成的投资组合,并采用小头寸规模。
他的大多数策略思想都基于他在市场中观察到的特定形态。其中之一便是宽幅周中反转(Wide-Range Midweek Reversal)策略。该策略基于以下理念:价格动能无法在任一方向上维持太久,其主要原因是市场参与者会变得筋疲力尽。
毕竟,当出现一轮持续的运动后,剩下还能继续买入或卖出的人已经大大减少。比如说,如果已经连续下跌了8天,市场上还有谁剩下要卖呢?大多数剩下的持股者,都是经受住了熊市调整的坚定分子,甚至可能正在加仓买入。
宽幅周中反转策略基于这样一种理念:一周的行情往往始于某个方向的动能,但周初的动能越强,发生反转的可能性就越大。因此,该策略着眼于在周三寻找趋势发生变化的机会,阿图彻认为这一天是周初动能发生反转的理想时机。
接下来,让我们看看该策略的交易规则,以及如何将其编写为TradingView代码。
宽幅周中反转策略的交易规则
做多入场规则:在周三开盘时买入,条件是从周一最高价到周二最低价的波幅,是10日ATR的1.5倍(即波幅比平均波幅高出50%),并且周二的最低价低于周一的最低价。做多离场规则:在日线图上出现两个连续的上涨收盘后,平掉多头仓位。做空入场规则:在周三开盘时做空,条件是周二的收盘价比上周五的收盘价高出1%,并且周三的开盘价高于周二的收盘价。做空离场规则:在出现第二个连续的下跌收盘时,回补空头仓位。
宽幅周中反转策略的一个有趣特点是,其多头和空头的逻辑是不对称的。阿图彻在他的书中并未解释为何如此设计,但这很可能与他认为做空并非简单的反向做多的理念有关。
为TradingView编写宽幅周中反转策略
现在,让我们将上述交易规则转化为一个功能完备的TradingView策略。一个有效的方法是使用模板,它能将一项大任务分解成更小、更易于管理的工作区块,也能为我们的思路增加一些结构性。
这是我们将用来编写宽幅周中反转策略的模板:
//@version=5
// 步骤1. 定义策略设置
// 步骤2. 计算策略所需指标值
// 步骤3. 定义多头交易条件
// 步骤4. 定义空头交易条件
// 步骤5. 在图表上输出数据
// 步骤6. 提交入场订单
// 步骤7. 提交出场订单
如果你想跟随本文一起操作,请在TradingView的Pine编辑器中创建一个新的策略脚本,并将上述模板粘贴进去。
为了让你对我们即将编写的代码有个直观的认识,以下是最终完成的宽幅周中反转策略在图表上的样子:
现在,就让我们开始动手,将上述交易规则转化为一个功能完整的TradingView策略脚本。
步骤1:定义策略设置和输入选项
在第一步,我们需要配置策略的整体属性,并创建用户可调的参数设置。首先,我们定义策略的基础设置:
// 步骤1. 定义策略设置
strategy(title="宽幅周中反转策略", overlay=true,
pyramiding=0, initial_capital=100000,
default_qty_type=strategy.fixed,
default_qty_value=100,
commission_type=strategy.commission.cash_per_order,
commission_value=8, slippage=2)
我们通过 title 参数为策略命名。overlay=true 使策略直接叠加在主图表上。根据交易规则,我们通过 pyramiding=0 禁止加仓。initial_capital 将策略的初始资金设为100,000。订单数量(default_qty_type)设为固定手数(strategy.fixed),每笔交易固定为100股(default_qty_value=100)。在交易成本方面,我们设定每笔单边订单(commission_type)收取8个单位的固定佣金(commission_value=8),并假设市价单和止损单会产生2个最小跳动点的滑点(slippage=2)。这里的成本设置相对悲观,但高估交易成本总比低估要安全。
我们还为策略脚本创建了几个输入选项:
atrLen = input.int(10, title="ATR周期")
atrMult = input.float(1.5, title="ATR倍数", step=.1)
closePerc = input.float(1, title="空头更高收盘价 %", step=.25)
我们为每个策略参数都创建了对应的输入项,这样就可以在不修改代码的情况下方便地进行手动配置。我们创建了一个名为”ATR周期”的整数输入,一个名为”ATR倍数”的浮点数输入,以及一个名为”空头更高收盘价 %”的浮点数输入,并为它们设定了合理的默认值。
步骤2:计算交易策略所需的值
接下来,我们计算策略运行所需的核心指标值。在这一步中,我只包含了一个值的计算:
// 步骤2. 计算策略所需值
atrValue = ta.atr(atrLen)
我们使用 ta.atr() 函数来计算10周期的平均真实波幅(ATR),并将其结果储存在 atrValue 变量中供后续使用。
步骤3:编写多头交易规则
接下来,我们将多头交易规则转化为Pine Script代码,具体如下:
// 步骤3. 定义多头交易条件
enterLong = dayofweek == dayofweek.tuesday and
low < low[1] and
low * (atrValue * atrMult) < high[1]
exitLong = close > close[1] and
close[1] > close[2] and
strategy.position_size > 0
我们定义了两个布尔变量:enterLong 和 exitLong。enterLong 的值为 true 的条件有三个,必须同时满足:一是当前必须是星期二;二是星期二的最低价必须低于星期一的最低价;三是星期二的最低价必须比星期一的最高价低1.5倍的10周期ATR。
exitLong 的值为 true 的条件也有三个:一是出现连续两个更高的收盘价;二是当前策略确实持有多头仓位(strategy.position_size > 0)。
步骤4:编写空头交易条件
接下来,我们编写空头交易的条件。代码如下:
// 步骤4. 定义空头交易条件
enterShort = dayofweek == dayofweek.tuesday and
(close - close[2]) / close[2] > closePerc * 0.01
exitShort = close < close[1] and
close[1] < close[2] and
strategy.position_size < 0
enterShort 的值为 true 的条件有两个:一是当前是星期二;二是星期二的收盘价比上周五的收盘价高出指定的百分比(默认为1%)。
这里需要注意的是,虽然交易规则是在周三开盘做空,但我们的代码是在周二判断条件。这是因为TradingView策略在历史回测时,是在K线收盘后进行计算的,此时发出的订单最快也要在下一根K线(即周三)的开盘时才能成交,所以这种写法在功能上是等效的。
exitShort 的值为 true 的条件则是在持有空仓的情况下,出现连续两个更低的收盘价。
同样,原规则要求在收盘时回补,但由于回测机制的限制,我们的代码实际上会在满足条件的K线之后的开盘时平仓,这会带来一个小的延迟。
步骤5:输出数据并可视化信号
接下来,我们在图表上高亮显示策略的交易信号,以便直观地验证策略行为:
// 步骤5. 输出策略数据
bgColour = enterLong ? color.new(color.green, 90) :
enterShort ? color.new(color.red, 90) :
na
bgcolor(bgColour)
我们通过 bgcolor() 函数,在 enterLong 信号出现时将背景染成绿色,在 enterShort 信号出现时染成红色。
步骤6:提交开仓订单
现在,我们让策略真正开始交易。首先是开仓逻辑:
// 步骤6. 提交开仓订单
if enterLong
strategy.entry("EL", strategy.long)
if enterShort
strategy.entry("ES", strategy.short)
当 enterLong 为真时,我们调用 strategy.entry() 开立一个ID为”EL”的多头仓位。当 enterShort 为真时,则开立ID为”ES”的空头仓位。
步骤7:提交平仓订单
接下来,我们让策略根据平仓条件退出交易:
// 步骤7. 提交平仓订单
if exitLong and not enterShort
strategy.close("EL", comment="XL")
if exitShort and not enterLong
strategy.close("ES", comment="XS")
当 exitLong 为真时,我们调用 strategy.close() 来平掉ID为”EL”的多头仓位。我们还额外加入了 not enterShort 这个条件,是为了处理一个特殊的边缘情况:防止在平掉多仓的同时又开立了新的空仓。如果平多信号和开空信号恰好在同一根K线上出现,不加此限制可能会导致最终的空头仓位是预期规模的两倍。空头平仓的逻辑与此类似。
宽幅周中反转策略的表现
让我们先从一个积极的观察开始。这个策略能够识别出一些盈利的交易机会,在这些机会中,价格动能确实在周三发生了反转。
例如,下图中的短期趋势在周三发生了逆转。策略不仅成功地交易了这个机会并获利,还在接近底部的位置回补了仓位:
然而不幸的是,该策略也有不少失手的时候。在大约一半的情况下,周三并不会发生动能反转。
例如,下方的图表显示了策略在一个强劲的上升趋势开始时是如何做空的。更糟糕的是,脚本在上升趋势的顶部附近才平掉空仓,这导致了一笔巨大的亏损:
下方的表格展示了该策略在纳斯达克100 ETF(QQQ)和标普500 ETF(SPY)上的迷你回测结果。两个品种的结果均为负。虽然该策略的胜率并非惨不忍睹,但其核心问题在于,亏损交易的平均亏损额大于盈利交易的平均盈利额。这表明,阿图彻的宽幅周中反转策略违背了一条基本的交易准则:截断亏损,让利润奔跑。
| 表现指标 | QQQ (纳斯达克100 ETF) | SPY (标普500 ETF) |
|---|---|---|
| 首次交易 | 1999-03-17 | 1993-02-17 |
| 最后交易 | 2018-09-18 | 2018-07-23 |
| 时间框架 | 日线 | 日线 |
| 净利润 | -$5,739 | -$2,036 |
| 总利润 | $17,206 | $25,951 |
| 总亏损 | -$22,945 | -$27,987 |
| 最大回撤 | $7,322 | $5,930 |
| 盈利因子 | 0.75 | 0.927 |
| 总交易数 | 319 | 301 |
| 胜率 | 53.61% | 49.83% |
| 平均每笔交易 | -$17.99 | -$6.76 |
| 平均盈利交易 | $100.62 | $173.01 |
| 平均亏损交易 | -$155.03 | -$185.34 |
| 平均盈亏比 | 0.649 | 0.933 |
| 支付佣金 | $4,888 | $4,736 |
| 每笔订单滑点 | 2个跳动点 | 2个跳动点 |
改进思路与新策略方向
从上表可以看出,宽幅周中反转策略无疑需要进一步的打磨。以下是一些你可能会觉得有价值去探索的思路。
阿图彻并未澄清为何策略的多空逻辑不对称。或许,将多头和空头的逻辑互换,策略的表现会更好。至少,这样做能让我们更好地理解策略的行为。
阿图彻关于价格动能在周三反转的假设,似乎存在几个问题。这个假设认为动能从周一开始,但情况并非总是如此:前一周的价格动能可以轻易地延续到新的一周。这个假设还认为动能会在中途反转,但这与我们经常在趋势中看到的现象不符:一轮上升趋势可以持续很长时间,而随后的下跌趋势可能会在极短的时间内抹去所有涨幅。
因此,一个好的想法可能是,让宽幅周中反转策略减少对特定星期几的依赖,而更多地响应价格行为本身,无论它发生在星期几。
该策略的离场交易不依赖于星期几。但如果我们假设价格动能在周中反转,那么平仓的最佳时机或许是在周末附近,因为新的一周往往伴随着新的价格动能。让离场逻辑与策略试图交易的情境更一致,可能会改善其表现。
该策略目前没有止损订单来限制损失。虽然止损可能会降低策略的绝对回报,但其经风险调整后的回报可能会变得更好。而且,知道市场中有一个明确的止损来防范重大损失,也能让策略更容易被执行。
阿图彻没有讨论他选择这些参数的理由。它们可能是一个彻底优化的结果,也可能只是出于方便。无论如何,我们很可能需要自己探索哪些参数效果最好。
我们也可以通过加强风险管理来改善策略的表现。例如,限制策略的最大持仓规模,或者在达到最大资金回撤后停止交易。
完整代码:TradingView的宽幅周中反转策略
以下是宽幅周中反转策略的完整代码。关于更多细节和信息,请参阅上文的讨论。
//@version=5
// 步骤1. 定义策略设置
strategy(title="宽幅周中反转", overlay=true,
pyramiding=0, initial_capital=100000,
default_qty_type=strategy.fixed,
default_qty_value=100,
commission_type=strategy.commission.cash_per_order,
commission_value=8, slippage=2)
// --- 输入项 ---
atrLen = input.int(10, title="ATR周期")
atrMult = input.float(1.5, title="ATR倍数", step=.1)
closePerc = input.float(1, title="空头更高收盘价%", step=.25)
// 步骤2. 计算策略所需指标值
atrValue = ta.atr(atrLen)
// 步骤3. 定义多头交易条件
// 周二检查:周二低点<周一低点,且周一高点到周二低点的波幅 > 1.5*ATR
enterLong = dayofweek == dayofweek.tuesday and
low < low[1] and
(high[1] - low) > (atrValue * atrMult) // 注意:此代码与原文描述不完全一致,此处按代码直译
// 离场条件:连续两天收盘价上涨
exitLong = close > close[1] and
close[1] > close[2] and
strategy.position_size > 0
// 步骤4. 定义空头交易条件
// 周二检查:周二收盘相比上周五收盘上涨超过N%
enterShort = dayofweek == dayofweek.tuesday and
(close - close[2]) / close[2] > closePerc * 0.01
// 离场条件:连续两天收盘价下跌
exitShort = close < close[1] and
close[1] < close[2] and
strategy.position_size < 0
// 步骤5. 在图表上输出数据
// 以背景色高亮显示入场信号
bgColour = enterLong ? color.new(color.green, 90) :
enterShort ? color.new(color.red, 90) :
na
bgcolor(bgColour)
// 步骤6. 提交入场订单
if enterLong
strategy.entry("EL", strategy.long)
if enterShort
strategy.entry("ES", strategy.short)
// 步骤7. 提交出场订单 (带冲突检测)
if exitLong and not enterShort
strategy.close("EL", comment="XL")
if exitShort and not enterLong
strategy.close("ES", comment="XS")









