量化策略开发,高质量社群,交易思路分享等相关内容
你是否应该在五月卖出,还是等到夏天晚些时候,甚至干脆不卖?十月是重新入市的最佳时机吗?股市中有哪些表现最好的和最差的月份?是否真的存在统计显著的季节性模式?我们能否通过结合其他技术指标来改进季节性策略?继续阅读,你将找到答案。
“五月卖出,十月买入保持理智”是金融界非常知名的谚语之一,源于股市在夏季通常表现不佳的理论。
历史数据通常支持这一流行的格言。根据公司金融研究所的一项研究(参见文末的"进一步阅读"),自1945年以来,标普500指数在11月至次年4月期间的累计六个月平均收益为6.7%,而在5月至10月期间的平均收益约为2%。
Jacobsen等人的更深入的学术研究,考察了从1693年开始,来自全球65个国家的所有可用股市数据,证实了在11月至次年4月期间,股市的平均回报率比5月至10月期间高出约4%。
冬季4%的超额收益对我来说好得令人难以置信。考虑到这个说法已经存在了很长时间,这些统计数据可能已经随着时间发生了变化。因此,我决定使用更新的数据和不同的时间段进行调查,同时考虑回撤和风险调整后的收益。接下来,我会介绍一个混合季节性系统,在文章第一部分的最佳季节性模式基础上,加入其他技术指标或条件,以提高其表现。
月度统计
季节性研究的第一步是分别考察每个月份,看看是否有些月份的股市回报显著不同于其他月份。在图1和图2中,你可以看到SPY ETF在过去10年和30年的月度表现。初始投资金额为每年固定的10万美元,不考虑利润复利、佣金、股息或利息。
图表 1:SPY ETF 从1992年1月1日到2021年12月31日的月度表现。
每个月的表现是通过在每年每个月的第一个交易日以开盘价投资10万美元到SPY ETF中,然后在下个月第一个交易日以开盘价卖出计算得出的。在表格的第三列是年复合收益率百分比 (CAR),第四列显示了胜利年份和失败年份的比率。第六列是盈亏比 (PF),表示总利润与总亏损的比值。第六列还显示了最大回撤,下一列是溃疡指数,这也是一种回撤统计指标,其计算方法是将所有回撤值平方后相加,再开平方根,然后除以时间段数。该指数越低越好。最后一列展示了波动指数 (VIX) 在该月内的最高日内数值。
✦
•
✦
图表 2:SPY 过去10年的平均月度收益
该图展示了SPY ETF从2012年1月1日到2021年12月31日期间的月度表现。关于各列标题的说明和测试方法,请参见图表1下的说明。
✦
•
✦
从测试结果可以明显看出,有些月份的表现显著优于平均水平。
在风险调整的基础上,结合两个时间段考虑,4月和11月是表现最好的月份。11月略胜一筹,因为它在过去10年中总是盈利,在过去30年中则有83%的时间盈利。
季节性因素在这里起到了重要作用,因为感恩节和圣诞节期间的假日购物增加通常会推高股价。由于预期第一季度收益的发布,4月股市总体上也会上涨。
总体来看,在市场中表现最好的月份是4月、7月、10月和11月。这些月份在10年模拟期间平均获得2.3%的收益,并在85%的时间内产生了正收益(见图2),而月度平均回报为1.1%。8月和9月是最差的月份,整体回报为负,且波动性最高。
低成交量和市场参与者的缺乏,因为暑假,可能是造成这一现象的原因。在低成交量期间,即便是微小的消息也可能导致市场下跌。然而,7月是个例外,因为通常第二季度收益优于预期,结果总体上是上涨的。
最不可预测的月份是1月、3月、6月、8月和9月,这些月份只有56%的时间产生正回报。波动性最大的月份是2月、3月、6月、8月和9月,在过去30年中平均回撤为-30%。3月在过去10年中平均亏损,这主要是由于2020年3月的新冠疫情导致的急剧下跌。最后,在最后一列中,你可以看到每个月的VIX的最高值。
从数据中可以看出,VIX与回撤之间几乎没有相关性,也与月份回报之间没有明显关联。事实上,在表现最好的4月和11月,波动性分别高达60和81。这有点违反直觉,因为通常情况下,较高的隐含波动性往往伴随着更大的回撤和负收益。然而,仔细查看VIX的图表后,我发现过去30年中,VIX超过55的情况只有两次,一次是2008年金融危机,另一次是2020年新冠疫情引发的市场暴跌。图1中10月至12月的高VIX值与2008年金融危机相关,而3月至4月的高VIX值与2020年的疫情危机相关。而4月的突出回报恰好与两次危机结束的时间相吻合,超卖反弹也助推了4月的优异表现。
“五月卖出”:真相还是神话?
“五月卖出”这一日历效应是偶然现象,还是值得观察的现象?股市回报的历史季节性值得投资者注意和理解。
通常,“五月卖出”的解读是5月1日(劳动节)卖出,10月31日(万圣节)再买入,但这显得有些随意,因为原始谚语中并没有明确具体日期。因此,我决定测试所有可能的组合,即在5月和10月的开头或结尾进行买卖。
根据图1中的统计研究,真正的市场危险月份是8月和9月,具有负收益和高波动性,因此我决定对原始规则进行修改,在8月卖出,10月再买入。
✦
•
✦
为了本次模拟,我使用了SPY ETF过去10年和30年的日线历史数据。在开盘时执行买入信号,在收盘时执行卖出信号。为了简化,绩效计算中没有考虑股息或利息。无论如何,这对结果的影响很小,因为当市场空仓时获得的利息部分抵消了错过的股息。整个测试过程中使用了当前的折扣佣金率,初始投资金额为10万美元。为了与买入并持有策略进行合理比较,收益是复利计算的。
在图3的表格中可以看到,“五月卖出”策略只有在五月底卖出并在十月初买入的情况下,表现优于买入并持有。然而,图4中的最新统计数据表明,自2011年以来,这种情况不再成立,因为过去10年内没有任何“五月卖出”策略能够超过买入并持有。
图表 3:“五月卖出”季节性策略的30年表现
该图展示了SPY ETF从1991年5月31日到2021年5月31日期间的表现。关于左侧列统计数据的说明,请参见图表1下的说明。不同的组合方式进行了测试,分别在五月或十月的开头或结尾进行买卖操作。可以看到,“五月卖出”策略只有在五月底卖出并在十月初再次买入时,表现优于买入并持有策略。
✦
•
✦
图表 4:“五月卖出”季节性策略的10年表现
该图展示了SPY ETF从2011年5月31日到2021年5月31日期间的表现。关于左侧列统计数据的说明,请参见图表1下的说明。不同的组合方式进行了测试,分别在五月或十月的开头或结尾进行买卖操作。自2011年以来,没有任何“五月卖出”策略能够超过买入并持有策略的表现。
✦
•
✦
“五月卖出”的流行解释是,从五月初到十月底的表现最差,在30年和10年的测试中,分别比买入并持有低1.8和4.7个百分点。这可能是因为这个说法已经存在了很长时间,季节性趋势在此期间已经演变或发生了变化。
另一方面,有效的方法似乎是如果你在八月和九月期间退出市场(见图3和图4最后一列的统计数据),你将比买入并持有的表现高出一个百分点以上,并且最重要的是,在过去的10年里,所有交易都获利(见图4),而如果你在2012年初买入SPY ETF并在2021年底卖出,盈利年份的比例仅为80%。
RS VolatAdj EMA与EMA
设计此系统的主要目标是通过增加其他技术指标来改进季节性系统,但同时将交易次数和资本利得税降到最低。在2021年7月的文章中,我使用波动性条件来改进买入并持有策略,通过在高波动期关闭仓位,通常可以避免重大市场抛售。结果非常鼓舞人心,因此我决定再次使用这些条件来改进季节性系统。在这种情况下,我同时使用了VIX隐含波动率条件和15天平均真实波幅(ATR)来计算历史波动率。
在指定波动率规则时,我只使用了从最近的低值开始的波动率变化,而不是指标的绝对值,因为每次重大抛售都有其独特的特征,波动率的阈值水平在每次情况中都不同。
这改善了季节性策略,但在2001年和2008年持续的熊市中错过了一些关键的退出信号。因此,我决定添加一个附加条件来处理这些情况。我的第一个想法是尝试使用趋势指标,但这并没有提高整体表现,主要原因是重新进入市场的信号来得太晚。此外,它还产生了更多的交易,增加了交易成本和资本利得。我的第二个选择是尝试长期资金流动条件。我测试了多个资金流动指标,发现VFI在整个2008年市场崩盘期间(见图5)读数极低(< -20),并且在2001-2002年熊市期间也持续为负,因此我决定将其添加到退出条件中。
图表 5:包含VIX和VFI的混合季节性策略信号在SPY上的表现
该图展示了SPY ETF从2007年7月到2009年7月的日线图,黑色线条叠加了VIX。中间窗口绘制了15天平均真实波幅(ATR),底部窗口绘制了130天交易量流动指标(VFI)。绿色表示正值,橙色表示负值但高于-20,红色表示低于-20的极度负值。可以注意到,VFI在2007年10月跌破0,并在负值区域持续到2009年6月。2008年2月,它跌破-20,除了2008年5月的短暂波动外,它在极度负值区间(-20到-35之间)持续振荡到2009年3月。
✦
•
✦
VFI(交易量流动指标)是我在2004年6月文章中首次介绍的一种自定义资金流动指标。它是一个长期指标,可以检测股票在过去六个月或130天内的资金流动。一般来说,正值代表看涨,负值代表看跌,但指标的方向也很重要。
结合上述最佳季节性系统、波动率和VFI条件,我制定了以下规则:
买入规则
季节性:表现最佳的是“八月卖出”策略(见图3和图4的最后一列),因此我使用这个系统作为季节性规则。因此,季节性买入在十月初(每月第一个交易日的开盘时)进行,并且如果波动率和VFI未达到下述关键阈值,则会发出买入信号。在高波动时期,季节性交易会推迟到月末或年末,直到波动性减弱。
波动性:如果VIX上涨超过60%,且过去25天内15天ATR上涨超过90%,则阻止所有季节性交易。VIX的阈值参数(60%)是通过优化确定的,而ATR是VIX阈值的1.5倍。
卖出规则
季节性:季节性交易在八月的第一个交易日开盘时关闭。
高波动性:如果VIX上涨超过2倍(120%)的入场阈值(60%),则关闭季节性交易。
资金流动(VFI):如果VFI跌破关键值(-20),且其10日均线指向下,则触发卖出信号。波动率和VFI退出信号在次日开盘时执行。
测试方法
对于本次模拟,我使用了从1991年7月30日至2021年7月30日的SPY ETF的每周历史数据。我的主要目标是将该系统与买入并持有策略进行比较,但遇到了一些问题:基于恒定美元交易规模的系统结果不能直接与买入并持有的结果进行比较,因为买入并持有的头寸大小随着时间的推移而增加。为了客观地比较系统与买入并持有的结果,我将交易规模设定为权益的百分比。为简化起见,股息未纳入绩效计算中。在任何情况下,这对结果的影响都很小,因为该系统80%以上的时间都在市场中。此外,在卖出和下一个买入信号之间持有现金不产生利息,这部分抵消了错过的股息。测试过程中使用了典型的折扣佣金率(每股0.01美元),初始投资金额为10万美元。
系统评估
在图6的表格中,您可以看到10年、20年和30年回测的模拟结果。混合季节性系统轻松超越了买入并持有和最佳季节性策略,风险不到一半,从而产生了更平滑的权益曲线。表现最好的回测是20年测试,其年化收益几乎是买入并持有的两倍,风险则不到一半。最赚钱的是最近的10年测试,年化收益率为16.1%,但这仅比买入并持有的年化收益率(13%)高出约3个百分点或24%。在这段时间内,超过买入并持有并不容易,因为美国股市处于一个长期牛市中,只有少数短暂的回调。然而,从2001年到2009年,我们经历了两次主要的熊市。
图表 6:混合季节性策略表现
该图展示了混合季节性系统在SPY ETF上的表现,涵盖了截至2021年7月30日的过去10年、20年和30年的期间。该系统表现远远优于买入并持有策略,产生了1.5到3倍的利润,且风险明显更低。
✦
•
✦
最严重的回撤发生在2003年初,主要是由于2000年互联网泡沫破裂后持续下行趋势中的两笔季节性交易积累的损失。尽管这些交易关闭得较晚,但资金流动(VFI)条件最终将其关闭。
在图7的图表中,您可以看到系统在过去五年期间产生的信号。除了八月初的季节性卖出信号外,还有两个额外的波动率卖出信号,但没有资金流动退出信号。现有卖出信号的时机相当不错,特别是最近一次(2020年2月26日),它成功让你避免了整个新冠疫情抛售期。然而,2018年11月的特朗普关税短暂抛售期间没有发出退出信号。这导致了最新10年测试中的最大系统回撤(-26%)和最大交易回撤(-19.8%)。在这种情况下,波动率退出信号未能触发,因为VIX未达到触发波动率退出所需的最低阈值。此外,VFI在这一短暂(三个月)的下跌期间从未跌破关键的熊市阈值(-20)。这是因为VFI是一个长期指标,考虑了6个月的成交量数据。降低最低阈值未能改善整体测试结果,因为它触发了后续多余且无利可图的进场和退出信号。
图表 7:包含VIX和VFI的混合季节性策略信号在SPY上的表现
该图展示了SPY ETF从2016年7月到2021年7月的日线图,黑色线条叠加了VIX指数。图中可以看到蓝色的买入信号和红色的卖出信号。底部窗口绘制了蓝色的权益线和红色的回撤线,中间窗口显示了130天的交易量流动指标(VFI)。
设计这个系统时,我没有添加任何资金管理规则,因为我的目的是将其与买入并持有和纯季节性系统的表现进行比较。出于好奇,我添加了一个5%的追踪止损和10天的重新进入延迟。这个简单的规则显著改变了表现:在30年测试中,净利润翻倍至600万美元,但盈利交易的百分比从89%大幅下降至51%,且盈亏比从19.2降至3.7。这是因为追踪止损产生了额外的154笔新交易,使该系统更适合频繁交易者。
最后的思考
最近的测试表明,五月卖出为时过早。毕竟,没有人会休息六个月,尤其是基金经理们。如果你打算卖出股票,最好的时机是七月底或八月初,因为八月和九月通常是表现最差的月份。
这也带来了一个更广泛的投资教训:你不能指望每年都遵循统计上显著的模式,还需要考虑其他因素。例如,新冠疫情b'p大流行导致2020年2月至3月期间股市下跌35%,而这一时期通常表现为正收益;而2020年8月,尽管统计数据显示收益较差,但表现却出色。无论如何,如果历史重演,在八月份采取更保守的立场可能会被证明是明智的策略。
当基金经理决定他们的股票分配时,最重要的基本面考虑因素是估值。问题在于,高估值或超买的情况可能会持续比你想象的更长时间。更好的方法是将季节性系统与技术指标相结合。
本文描述的混合季节性系统通过最少的额外交易轻松超过了买入并持有和季节性策略。我毫不怀疑,添加资金管理条件(如止损)或更多技术条件可以进一步改进该系统。
策略代码
指标:
Params
Numeric Period(130);
Numeric Coef(0.2);
Numeric VCoef(2.5);
Numeric SmoothingPeriods(3);
Numeric MA(30);
Vars
Series<Numeric> TP;
Series<Numeric> Inter;
Series<Numeric> VInter;
Series<Numeric> CutOff;
Series<Numeric> VAve;
Series<Numeric> VMax;
Series<Numeric> VC;
Series<Numeric> MF;
Series<Numeric> DirectionalVolume;
Series<Numeric> myVFI;
Series<Numeric> myVFI1;
Series<Numeric> myVFI2;
Events
OnBar(ArrayRef<Integer> indexs)
{
TP = (High + Low + Close) / 3;
If(TP > 0 && TP[1] > 0)
{
Inter = Ln(TP) - Ln(TP[1]);
}
Else
{
Inter = 0;
}
VInter = StandardDev(Inter, 30);
CutOff = Coef * VInter * Close;
VAve = Average(Vol, Period);
VMax = VAve * VCoef;
VC = IIF(Vol < VMax, Vol, VMax);
MF = TP - TP[1];
DirectionalVolume = IIF(MF > CutOff, VC, IIF(MF < -CutOff, -VC, 0));
myVFI1 = Summation(DirectionalVolume, Period) / VAve;
If(VAve <> 0)
{
myVFI = myVFI1;
}
Else
{
myVFI = 0;
}
myVFI2 = XAverage(myVFI, SmoothingPeriods);
If(SmoothingPeriods > 0)
{
myVFI = myVFI2;
}
PlotNumeric("VFI", myVFI);
PlotNumeric("0", 0);
PlotNumeric("MA", XAverage(myVFI, MA));
}
✦
•
✦
策略:
Params
Numeric SellMonth(5); // 5 to 8 by 1
Numeric VIXUpMax(60); // 50 to 60 by 10
Numeric Crit(-20); // -20 to -15 by 5
Numeric K(1.5); // 1.3 to 1.7 by 0.2
Vars
Series<Numeric> VIXDown;
Series<Numeric> VIXUp;
Series<Numeric> ATRDown;
Series<Numeric> ATRUp;
Series<Numeric> RefSymbol;
Series<Numeric> VolatC;
Numeric Period(130);
Numeric Coef(0.2);
Numeric VCoef(2.5);
Series<Numeric> Cutoff;
Series<Numeric> MF;
Series<Numeric> Inter;
Series<Numeric> VInter;
Series<Numeric> Vave;
Series<Numeric> Vmax;
Series<Numeric> highx;
Series<Numeric> lowx;
Series<Numeric> downx;
Series<Numeric> upx;
Series<Numeric> Vc;
Series<Numeric> VCP;
Series<Numeric> VFI1;
Series<Numeric> VFI;
Series<Bool> BuySignal;
Series<Bool> VolCondition;
Series<Bool> SellMF;
Series<Bool> SellSeasonal;
Series<Bool> SellVolatility;
Events
OnBar(ArrayRef<Integer> indexs)
{
VolatC = Volatility(C,30);
highx=Highest(VolatC, 25);
downx=(VolatC / highx[1] - 1) * 100;
If(highx[1] <> 0)
{
VIXDown = downx;
}
Else
{
VIXDown = 0;
}
lowx=Lowest(VolatC, 25);
upx=(VolatC / lowx[1] - 1) * 100;
If(lowx[1] <> 0)
{
VIXUp = upx;
}
Else
{
VIXUp = 0;
}
ATRUp = (AvgTrueRange(15) / Lowest(AvgTrueRange(15), 25) - 1) * 100;
Inter = Ln((High + Low + Close) / 3) - Ln((High[1] + Low[1] + Close[1]) / 3);
VInter = StandardDev(Inter, 30);
Cutoff = Coef * VInter * Close;
Vave = Average(Vol, Period);
Vmax = Vave * VCoef;
Vc = Min(Vol, Vmax);
MF = (High + Low + Close) / 3 - (High[1] + Low[1] + Close[1]) / 3;
VCP = IIF(MF > Cutoff, Vc, IIF(MF < -Cutoff, -Vc, 0));
VFI1 = Summation(VCP, Period) / Vave;
VFI = XAverage(VFI1, 3);
VolCondition = (VIXUp < VIXUpMax || ATRUp < K * VIXUpMax) && VFI > Crit;
Commentary("VIXip:"+Text(VIXUp)+"VIXUpMax:"+Text(VIXUpMax)+"ATRUp:"+Text(ATRUp)+"VFI:"+Text(VFI)+"Crit:"+Text(Crit));
BuySignal = (Month() >= 10 || Month() < SellMonth) && VolCondition[1];
SellSeasonal = Month() == SellMonth; // SEASONAL
SellVolatility = VIXUp > 2 * VIXUpMax; // VOLAT EXIT
SellMF = CrossOver(Crit, VFI) && Average(VFI, 10) < Average(VFI, 10);
If(BuySignal)
{
Buy(100, Close);
}
If(SellSeasonal)
{
Sell(100, Close);
Commentary("SELL SEASONAL");
}
Else If(SellVolatility[1] || SellMF[1])
{
Sell(100, Close);
Commentary("SELL VOLAT");
}
}