1 什么是量化择时
量化择时策略,简单来说,就是采用数量化分析方法,利用单个或多个技术指标的组合,来对交易标的股票或股票指数进行低买高卖的操作,期望获得超越简单买入持有策略的收益风险表现。
量化择时策略的核心是技术分析,更准确地来说,是客观型技术分析。客观型技术分析,是指其分析过程中所用到的分析方法,具有100%客观的定义标准,不含有任何主观定义的部分。
基于客观型技术分析的量化择时策略,对普通投资者具有一个很重要的意义,那就是具有很强的可传授性和可复制性 ,任何人只要花费时间精力去理解、去掌握了这些有效的量化择时策略,都可以将它们付之于实盘,获得同样令人满意的投资业绩。
正如巴菲特在谈论市场时说:“我们所经历的上个世纪反复证明,股票市场的非理性是周期性爆发的。这强烈暗示投资者应尽力去学会应对下一个股票市场的非理性爆发。而这需要的是一剂解毒剂,我认为这剂解毒剂就是量化分析。如果你定量分析,你并不一定会出色,但是你也不会坠入疯狂。”,所谓量化择时即为:根据量化指标,来选择买卖的时机。量化择时分市场整体择时、板块行业轮动择时及个股的择时。
量化择时的意义重大,如果能选择牛市、规避熊市,将能够获得非常高超额收益。尤其是在系统性风险较高、波动性比较大的、相关性较强的新兴A股市场,尤为有效。根据A股市场数据计算,从1991年1月到2010年7月的234个月月平均收益为2.18%,去掉最差的十个月(占总时间的4.27%),则收益提高到4.23%;如果去掉最好的10个月,则收益降到0.01%。如果投资者从2006年初的市场底部进入,则收益巨大;如果从2008年初进入,则亏损很多。可见选择时机进入是投资决策的首要目标,低风险区域进入能增大安全边际。
2 量化择时的策略
常见的客观性技术分析有:均线、MACD、RSI等,它们都具有一个共同点,就是都有明确的数学计算公式,只要给出计算公式,任何人计算出来的结果都是一样的,比如20日简单均线,不管是谁画出来的均线都是一样的。
这些客观型技术分析指标按性能及设计特征,归纳了八种量化择时方法: 趋势择时、 Hurst指数、SWARCH模型、异常指标择时、市场情绪择时、SVM分类、有效资金模型、牛熊线。
2.1 均线计算
我们结合金融软件具体查看一下该指标。目前各大金融软件基本都提供移动平均值的计算,如下截图展示了平安银行(000001.SZ) 2023年4月到6月的估价走势,在右上角展示了5日移动平均值(M5)、10日移动平均值(M10)、20日移动平均值(M20)、30日移动平均值(M30)。 为了进一步计算验证,本文拉取了20230501 - 20230630 交易日每天的日行情数据,根据收盘价计算,可以看出该计算结果与金融产品上展示的数据一致。
"""
使用tushare库来获取股票数据,并对数据进行处理和计算移动平均值。
"""
import tushare as ts
# https://tushare.pro/user/token
tushare_token = '0858b935f4518d9e56ffeb19910dc13e296291364ea1d7bd574eb84b'
ts.set_token(tushare_token)
pro = ts.pro_api(tushare_token)
# 个股数据 https://tushare.pro/document/2?doc_id=27
# 从tushare获取个股数据,从2023年5月1日到2023年6月30日期间获取股票代码为000001.SZ的数据,字段包括交易代码、交易日期和收盘价。
df = pro.daily(ts_code='000001.SZ', start_date='20230501', end_date='20230630', fields='ts_code,trade_date,close')
df = df.sort_values(by='trade_date')
# 使用.rolling()函数计算收盘价的移动平均值,分别为5天、10天、20天和30天,并将结果存储在M5、M10、M20和M30列中。
df['M5'] = df.close.rolling(window=5).mean().round(2)
df['M10'] = df.close.rolling(window=10).mean().round(2)
df['M20'] = df.close.rolling(window=20).mean().round(2)
df['M30'] = df.close.rolling(window=30).mean().round(2)
# 对数据按交易日期进行降序排序,以便按照日期从新到旧的顺序显示数据。
df = df.sort_values(by='trade_date', ascending=False)
print("前10条数据:\n", df.head(10))
df.to_csv('result.csv', index=False)
2.2 双均线策略
策略原理:使用两根均线,一根长周期均线,一根短周期均线。当短期均线从下往上穿越长周期均线的时候,买入;当短期均线从上往下穿越长周期均线的时候,卖出。前文介绍的Granville 八大法则也是一种双均线策略,其短周期均线为M1(当日收盘价)。均线根据不同周期有以下分类:
短期均线:5、7、10,用于预测短期走势,MA5 和 MA10 又称为短期监测线;
中期均线:20、30、60,用于预测中期走势,MA20 和 MA30 又称为警戒线, MA60 则称之为生死线;
长期均线:120、250,用于长期走势,MA120 又称为确认线,MA250 则通常被看做反转线,又称为牛熊分界线。
策略缺陷:
滞后性:均线归根到底是一种平均值,在应用中存在的最大问题就是滞后性。当出现买入卖出信号时,最佳时机早已过去。举例来说,如果A股票最新价格出现了较大的涨幅,股价和均线都上涨,但均线的速度慢于股价上涨速度。此时,从形态上来看,金叉出现,为买入信号。次日,股价回调,股价下降的速度快于均线下降的速度,形成死叉,为卖点。这样一买一卖不仅没有盈利,反而出现亏损。
长短周期难以选择:如果两根均线的周期接近,比如5日线,10日线,这种非常容易缠绕,不停的产生买点卖点,会有大量的无效交易,交易费用很高。如果两根均线的周期差距较大,比如5日线,60日线,这种交易周期很长,趋势性已经不明显了,趋势转变以后很长时间才会出现买卖点。也就是说可能会造成很大的亏损。所以两个参数选择的很重要,趋势性越强的品种,均线策略越有效。
策略优化尝试方向:
使用加权移动平均值:均线策略的一大缺陷是指标具有滞后性,因此可以使用加权移动平均值代替移动平均值。计算时将短期(如昨天)的权重增大,以加强指标的敏感性。
不局限于收盘价:可以尝试将最高价、最低价等加入到加权移动平均值的计算中。
自适应调整均线周期:价格走势进入单边上涨趋势,自适应均线自动缩短周期,采用短期均线,转为向上移动;市场走势进入横盘震荡,自适应均线自动延长周期,采用长期均线,转为横向移动。
双均线择时策略代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tushare as ts
# 设置tushare token
# tushare_token = 'your_tushare_token'
tushare_token = '0858b935f4518d9e56ffeb19910dc13e296291364ea1d7bd574eb84b'
ts.set_token(tushare_token)
pro = ts.pro_api()
# 获取股票数据
df = pro.daily(ts_code='000001.SZ', start_date='20210101', end_date='20230630', fields='trade_date,close')
# 计算短期均线和长期均线
df['MA5'] = df['close'].rolling(window=5).mean()
df['MA20'] = df['close'].rolling(window=20).mean()
# 生成交易信号
df['signal'] = np.where(df['MA5'] > df['MA20'], 1, -1)
# 计算当天持仓状态
df['position'] = df['signal'].diff()
# 回测策略
df['return'] = df['close'].pct_change() # 计算每日收益率
df['strategy_return'] = df['position'].shift() * df['return'] # 计算每日持仓收益
# 计算累计收益率
df['cumulative_return'] = (1 + df['strategy_return']).cumprod()
df["trade_date"]=pd.to_datetime(df["trade_date"],format="%Y%m%d")
df= df.set_index(df.columns[0])
# 可视化结果
plt.plot(df['cumulative_return'])
plt.xlabel('Date')
plt.ylabel('Cumulative Return')
plt.title('Dual Moving Average Strategy')
plt.xticks(rotation=100)
plt.show()
在这个示例代码中,我们使用了tushare库获取了股票代码为000001.SZ的股票数据。然后,计算了其中的短期均线(5日均线)和长期均线(20日均线)。根据这两条均线的交叉情况,生成了买入和卖出的信号。如果短期均线在长期均线之上,产生买入信号(1),如果短期均线在长期均线之下,产生卖出信号(-1)。
接下来,我们根据这些信号计算了每天的持仓状态,持仓状态的变化显示了买入和卖出的时机。然后,通过计算每日收益率和策略的收益率,我们可以回测该策略的表现。最后,我们计算了累计收益率,并使用Matplotlib可视化了策略的表现。
请注意,这只是一个示例代码,你可以根据自己的需求和策略进行修改和优化。在实际应用时,还需要考虑交易成本、滑点、止损和止盈等因素。投资有风险,建议在实际操作之前进行充分的研究和回测。
2.3 MACD均线择时策略
MACD称为异同移动平均线(Moving Average Convergence and Divergence),是由指数均线演变而来的,由Gerald Appel在1979年提出,是一个比较常见的技术指标。MACD指标通过对金融资产价格的收盘价进行平滑处理,生成了两线一柱组合,其中两线是快速线DIF(Difference Line)和慢速线DEA(Signal Line),一柱是柱状图MACD。MACD指标是快速线DIF与慢速线DEA的差,可以反映出股票近期价格走势的变化强弱和能量,用于把握股票的准确买卖点。
短期EMA:短期(如,12天)的收盘价指数移动平均值,一般采用EMA(price, 12)
长期EMA:长期(如,26天)的收盘价指数移动平均值,一般采用EMA(price, 26)
DIF:短期EMA与长期EMA的差值,即DIF=EMA(price, 12)-EMA(price, 26)
DEA:DIF的多日(如,9天)指数移动平均值,即DEA=EMA(DIF, 9)
MACD:DIF线与DEA线的差,即MACD=DIF-DEA
依据MACD进行择时
当MACD从负数转向正数,快速线DIF自下而上穿过慢速线DEA时,形成金叉,即买入信号。
当MACD从正数转向负数,快速线DIF自上而下穿过慢速线DEA时,形成死叉,即卖出信号。
当MACD以大角度变化,表示快的移动平均线和慢的移动平均线的差距非常迅速的拉开,代表了一个市场大趋势的转变
上述用法是MACD指标最直接且最经典的用法,具有完全的客观判断标准,可以很方便的用计算机来实现,并对其进行历史测试。MACD还有其他许多用法,比如底背离、顶背离等,但这些用法中的主观判断成份很重,没有统一客观的使用标准,不同的人使用的结果可能大不一样,也难以用计算机来实现。
中证500ETF在2022年12月到2023年6月的走势如下图,MACD指标在下方的窗格。
相比双均线策略,MACD将两条线转换成了单个指标,更容易观察,并且避免了双均线策略里两条均线经常在震荡行情下经常发生交叉的现象出现,减少频繁发出假信号的缺陷,MACD适用于研判中长期走势。
MACD指标的核心功能在于度量均线势能的变化,或者说是股价的二阶差变化,当股价上涨开始乏力时,MACD指标往往能较早的发出卖出信号,使得策略能在股价开始拐头下跌时及时离场,这是MACD指标具有较好风险控制能力的原因。
但是,当遇到大型的趋势上涨行情时,MACD指标可能会错误地过早离场,虽然根据策略逻辑,之后还会继续发出买入信号,但难免错失一段利润,所以在趋势上涨行情中,MACD策略的投资收益会不如股票指数的自身涨幅。
import numpy as np
import pandas as pd
import talib as ta
# Joinquant数据下载API
from jqdatasdk import *
# 登录验证
auth("Your_phonenum", "Your_passwd")
import matplotlib.pyplot as plt
# 下载数据
# 下载2015-2018年的沪深300指数,频率为每天,只要收盘价
price = get_price("000300.XSHG", start_date="2015-01-01", end_date="2018-12-31", frequency="daily", fields=['close'])['close']
# 用python自带的tseries库中的pct_change()函数计算日收益率
ret = price.pct_change()
# 用talib库中的相应函数计算MACD指标
dif, dea, macd = ta.MACD(price)
# 只考虑MACD指标,MACD转正时开仓买入,转负时清仓
sig = (macd>0)
# sig滞后一期、去除空值、转换成整数
sig_lag = sig.shift(1).fillna(0).astype(int)
# sig_lag与股票日收益率相乘,即可得策略日收益率。python能自动对齐时间序列的日期。
sig_ret = sig_lag*ret
# 计算策略累计收益
cum_sig_ret = (1+sig_ret).cumprod()
# 把股票价格转换成从1开始,方便比较
price_norm = price/price[0]
# 简单起见,这里不考虑手续费,作图比较该策略和“买入-持有”策略的表现。
plt.figure(figsize=(18,8))
plt.plot(price_norm)
plt.plot(cum_sig_ret)
plt.legend(["benchmark", "strategy cumulative return"], loc="upper left")
plt.show()