量化之路

“五穷六绝”真的存在吗?

2019-03-14  本文已影响0人  伏星宇
技术性调整
五穷六绝七翻身是香港股市在1980年代至1990年代的一个都市传奇,据说这是当时的证券分析师参考历年香港股市的涨跌规律得出的结论:股指每逢5月的时候都会开始跌,到了6月更会大跌,但到了7月却会起死回生。
大洋彼岸,有实证研究表明,美国股票市场具有明显的“1月效应”,即1月份的平均收益率为正,且显著高于其他月份。 该现象最早由Wachtel(1942)发现,但直到1976年Rozef和Kinney才系统地将这一有趣的现象揭示出来,于是美国股市的“1月效应”开始引起金融界的注意。
反观国内,网上随便一搜,分析A股市场“五穷六绝”现象的文章比比皆是。
那么,A股市场真的也存在所谓的“月份效应”吗?

所谓“月份效应”,主要是指股票市场中存在某个或某些特定月份,平均收益率显著地异于其他各月平均收益率的现象。

同样是利用tushare获取数据,我们以上证综指(000001.SH)为例:

def get_data(code,start_date):
    df = pro.index_daily(ts_code = code,start_date = start_date, end_date = '2018-12-31')
    df.index = pd.to_datetime(df.trade_date)
    return df
df = get_data('000001.SH','1993-01-01')

这里有一个小技巧,直接将df的trade_date行通过pd.to_datetime()处理后作为索引,便于后续操作。
修改前df格式(已转置):

0 1 2 3 4
ts_code 000001.SH 000001.SH 000001.SH 000001.SH 000001.SH
trade_date 20171229 20171228 20171227 20171226 20171225
close 3307.17 3296.38 3275.78 3306.12 3280.46
open 3295.25 3272.29 3302.46 3277.84 3296.21
high 3308.22 3304.1 3307.08 3307.3 3312.3
low 3292.77 3263.73 3270.35 3274.33 3270.44
pre_close 3296.38 3275.78 3306.12 3280.46 3297.06
change 10.7874 20.6019 -30.3418 25.6636 -16.602
pct_chg 0.3272 0.6289 -0.9177 0.7823 -0.5035
vol 1.41587e+08 1.75372e+08 1.62675e+08 1.42435e+08 1.46894e+08
amount 1.70357e+08 2.0802e+08 1.98265e+08 1.74679e+08 1.77294e+08

修改后df格式(已转置):

trade_date 2017-12-29 2017-12-28 2017-12-27 2017-12-26 2017-12-25
ts_code 000001.SH 000001.SH 000001.SH 000001.SH 000001.SH
trade_date 20171229 20171228 20171227 20171226 20171225
close 3307.17 3296.38 3275.78 3306.12 3280.46
open 3295.25 3272.29 3302.46 3277.84 3296.21
high 3308.22 3304.1 3307.08 3307.3 3312.3
low 3292.77 3263.73 3270.35 3274.33 3270.44
pre_close 3296.38 3275.78 3306.12 3280.46 3297.06
change 10.7874 20.6019 -30.3418 25.6636 -16.602
pct_chg 0.3272 0.6289 -0.9177 0.7823 -0.5035
vol 1.41587e+08 1.75372e+08 1.62675e+08 1.42435e+08 1.46894e+08
amount 1.70357e+08 2.0802e+08 1.98265e+08 1.74679e+08 1.77294e+08

接下来就可以用收盘价df.close计算收益率了。
这里借鉴了其他文章的思路,用对数化的日收益率推算月收益率与年收益率。
这样做的好处是熨平股价曲线的波动并且没有损失信息。

#计算日对数收益率
def log_ret(df):
    logret = np.log(df/df.shift(1))[1:]
    return logret

#将日收益率转换为月收益率
def month_rate(logret):
    date=[]
    d=list(logret.index)
    for i in range(0,np.size(logret)):
        t=''.join([d[i].strftime("%Y"),d[i].strftime("%m"),"01"])
        date.append(datetime.strptime(t,"%Y%m%d"))
    y=pd.DataFrame(logret.values,date,columns=['月收益率'])
    ret_M=y.groupby(y.index).sum()
    return ret_M

#将日收益率转换为年收益率
def annual_rate(logret):
    year=[]
    d=list(logret.index)
    for i in range(0,np.size(logret)):
        year.append(d[i].strftime("%Y"))
    y=pd.DataFrame(logret.values,year,columns=['年收益率'])
    ret_Y=np.exp(y.groupby(y.index).sum())-1
    return ret_Y, year

#收益率计算
logret = log_ret(df.close)
ret_M = month_rate(logret)
ret_Y, years = annual_rate(logret)

接下来先看年化收益率,不妨通过matplotlib.pyplot画图更加直观:

#===============画年收益率图===============
fig, ax = plt.subplots()
bar_width = 0.7
opacity = 0.63
ax.axhline(0, color='black', lw=1)
bar1 = ax.bar(ret_Y.index, np.squeeze(ret_Y.values),
              alpha=opacity,
              color = 'r')
ax.set_xlabel('Year')
ax.set_ylabel('logReturns')
ax.set_title('logReturns per year(000001.SH)')
年化对数收益率

描述性统计如下:

def roydescribe():
    print(ret_Y.describe())
    #年收益率最高的五个年份
    print(ret_Y.sort_values('年收益率')[:5].T)
    #年收益率最低的五个年份
    print(ret_Y.sort_values('年收益率',ascending=False)[:5].T)
    #年收益率为正的年份,以及占比
    ratio=int(ret_Y[ret_Y['年收益率']>0].count().values)/len(ret_Y)
    print(f'上涨年份占比{ratio*100}%')

roydescribe()
count mean std min 25% 50% 75% max
年收益率 25.0 0.019707 0.455158 -0.565138 -0.246657 0.016461 0.189655 1.803621

收益率最低的5个年份:

2006 2007 2009 1996 2014
年收益率 -0.565138 -0.484958 -0.420205 -0.414999 -0.37043

收益率最高的5个年份:

2008 2011 2001 1994 2004
年收益率 1.803621 0.314954 0.305373 0.303213 0.220812

总体看来,上证综指收益率为正的的年份占比52.0%

接下来检验月份效应,主要思路是:
(1)取出每年特定月份的月收益率,记为df_m
(2)取出每年去除某特定月份的其他所有月份收益率,记为df_exm
(3)进行两对假设检验(Ha省略):
a. H0: 某月收益率的均值与总体收益率均值显著不同。(单总体t检验ttest_1samp)
b. H0: 某月收益率的均值与除此月外其他所有月收入的均值显著不同。(双总体t检验ttest_ind)

#t检验
t1, p1 = stats.ttest_1samp(df_m, np.mean(ret_M.values))
t2, p2 = stats.ttest_ind(df_m, df_exm,
                         equal_var = True)
t3, p3 = stats.ttest_ind(df_m, df_exm,
                         equal_var = False)
#检验结果写成一个dataframe
testdf = pd.DataFrame([t1, p1, t2, p2,t3, p3],
                      index = ['单样本t检验_t值','单样本t检验_p值','双样本t检验_t值(同方差)','双样本t检验_p值(同方差)','双样本t检验_t值(异方差)','双样本t检验_p值(异方差)'],
                      columns = range(1, 13))
print(testdf.T)

事实上检验b命题需要先检验是否存在异方差,这里由于样本容量小,决定同时检验异方差和同方差存在的情况。具体结果如下。

单样本t检验_t值 单样本t检验_p值 双样本t检验_t值(同方差) 双样本t检验_p值(同方差) 双样本t检验_t值(异方差) 双样本t检验_p值(异方差)
0.021206 0.983257 0.028681 0.977138 0.022565 0.982166
-2.401039 0.024454 -1.023598 0.306855 -2.080191 0.041892
-0.516011 0.610568 -0.582992 0.560340 -0.542541 0.591769
-1.163957 0.255882 -1.387305 0.166385 -1.228705 0.229646
0.633335 0.532504 0.719484 0.472407 0.666184 0.510794
1.474966 0.153221 1.659263 0.098115 1.550275 0.132371
-0.411840 0.684111 -0.335063 0.737813 -0.418050 0.678707
0.015390 0.987848 0.023460 0.981299 0.016476 0.986981
0.625214 0.537731 0.402170 0.687847 0.609384 0.545949
0.375261 0.710762 0.262375 0.793213 0.371749 0.712311
0.534583 0.597856 -0.485051 0.627996 -0.550306 0.586153
0.636998 0.530156 0.720739 0.471635 0.669826 0.508497
t检验p值情况图

经检验可以看出,只有2月份的收益率与所有月份组成的总体均值(异方差假设下的其他月份均值)之间的差异具有5%水平的统计显著性。通过画出1993-2018月收益变化图我们可以看出,2月份的收益率总体上是小于0的,这可能与2月是春节所在月份,资金周期性紧张有关。


1993-2018月收益变化

综上,通过统计检验可以看出,A股并不存在明显的所谓的“五穷六绝七翻身”现象。与主要发达国家的股票市场不同,A股运营时间短,市场不成熟的特点可能对股指造成扭曲,其中存在的趋势还有待进一步挖掘。


百度搜索“五穷六绝” 百度搜索“五穷六绝”

事实上亚洲金融风暴后,“五穷六绝”这个词,香港同胞已经很少提起了。

参考文章:https://www.jianshu.com/p/71a5e315d2c1

完整代码如下:

# -*- coding: UTF-8 -*-
import pandas as pd
import numpy as np
import tushare as ts
from datetime import datetime
from pylab import mpl
import matplotlib.pyplot as plt
import scipy.stats as stats

#正常显示画图时出现的中文和负号
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
pro = ts.pro_api()

#获取数据函数
def get_data(code,start_date):
    df = pro.index_daily(ts_code = code,start_date = start_date, end_date = '2018-12-31')
    print(df.head().T)
    df.index = pd.to_datetime(df.trade_date)
    print(df.head().T)
    return df

#计算日对数收益率
def log_ret(df):
    logret = np.log(df/df.shift(1))[1:]
    return logret

#将日收益率转换为月收益率
def month_rate(logret):
    date=[]
    d=list(logret.index)
    for i in range(0,np.size(logret)):
        t=''.join([d[i].strftime("%Y"),d[i].strftime("%m"),"01"])
        date.append(datetime.strptime(t,"%Y%m%d"))
    y=pd.DataFrame(logret.values,date,columns=['月收益率'])
    ret_M=y.groupby(y.index).sum()
    return ret_M

#将日收益率转换为年收益率
def annual_rate(logret):
    year=[]
    d=list(logret.index)
    for i in range(0,np.size(logret)):
        year.append(d[i].strftime("%Y"))
    y=pd.DataFrame(logret.values,year,columns=['年收益率'])
    ret_Y=np.exp(y.groupby(y.index).sum())-1
    return ret_Y, year

#获取数据
df = get_data('000001.SH','1993-01-01')
#对数收益率
logret = log_ret(df.close)
#对数月收益率
ret_M = month_rate(logret)
#对数年收益率
ret_Y, years = annual_rate(logret)

#===============画年收益率图===============
fig, ax = plt.subplots()
bar_width = 0.7
opacity = 0.63
ax.axhline(0, color='black', lw=1)
bar1 = ax.bar(ret_Y.index, np.squeeze(ret_Y.values),
              alpha=opacity,
              color = 'r')
ax.set_xlabel('年份')
ax.set_ylabel('收益率')
ax.set_title('年化对数收益率(000001.SH)')

#===============年收益率的描述性统计===============
def roydescribe():
    print(ret_Y.describe().T)
    #年收益率最高的五个年份
    print(ret_Y.sort_values('年收益率')[:5].T)
    #年收益率最低的五个年份
    print(ret_Y.sort_values('年收益率',ascending=False)[:5].T)
    #年收益率为正的年份,以及占比
    ratio=int(ret_Y[ret_Y['年收益率']>0].count().values)/len(ret_Y)
    print(f'上涨年份占比{ratio*100}%')

roydescribe()


#===============月份效应检验===============
'''
# 所谓“月份效应”,主要是指股票市场中存在某个或某些特定月份的平均收益率年复一年显著地异于其他各月平均收益率的现象。
# 有实证研究表明,美国股票市场表现为“1月效应”,即1月份的平均收益率为正,且显著高于其他月份的平均收益。
# 该现象最早由Wachte1(1942)发现,但直到1976年Rozef和Kinney系统地将这一异象揭示出来,“1月效应”才引起金融界的注意。
# 美国等发达国家对“月份效应”的讨论和研究已走向成熟,但是由于A股运行时间较短,并且由于文化差异(节假日安排)导致的交易时间不一致,其“月份效应”特征可能异于我国,因此仍有必要进一步挖掘和探讨。
# 检验思路:(1)将数据分成两组:m月份和其他月份;(2)检验两组数据的平均值是否相等
'''

#每年不同月份收益率
df_m = pd.DataFrame()
df_exm=pd.DataFrame()
for i in range (1, 13):
    ret = ret_M[ret_M.index.month == i]
    df_m[str(i)+'月份'] = ret['月收益率'].values

#月收益率描述性统计
print(df_m.describe().round(3))
#除了i月以外,剩余月份收益率
for i in range (1,13):
    ret=ret_M[ret_M.index.month!=i]
    df_exm['ex'+str(i)]=ret['月收益率'].values


#历年月收益率变化图
def drawret(ro, co, mo):
    axs[ro, co].axhline(0, color='black', lw=1)
    axs[ro, co].plot(range(1993, 2018), df_m[str(mo) + '月份'], color='b', alpha=0.7)
    axs[ro, co].set(title=str(mo) + '月份')
    axs[ro, co].grid()

fig, axs = plt.subplots(3, 4, sharey= True)
j=1
for a in range(0, 3):
    for b in range(0, 4):
        drawret(a, b, j)
        j += 1
plt.tight_layout()

#t检验
#检验H0:df_m = 0
t1, p1 = stats.ttest_1samp(df_m, np.mean(ret_M.values))
#检验H0:df_m = df_exm
t2, p2 = stats.ttest_ind(df_m, df_exm,
                         equal_var = True)

t3, p3 = stats.ttest_ind(df_m, df_exm,
                         equal_var = False)

#检验结果写成一个dataframe
testdf = pd.DataFrame([t1, p1, t2, p2,t3, p3],
                      index = ['单样本t检验_t值','单样本t检验_p值','双样本t检验_t值(同方差)','双样本t检验_p值(同方差)','双样本t检验_t值(异方差)','双样本t检验_p值(异方差)'],
                      columns = range(1, 13))
print(testdf.T)

#t检验p值情况图
fig, ax = plt.subplots()
plt.title('t检验p值情况图')
plt.plot(range(1, 13), p1, label = 'p(H0:MEAN(df_m) = MEAN(ret_M.values))')
plt.plot(range(1, 13), p2, label = 'p(H0:MEAN(df_m) = MEAN(df_exm),equal_var = True))')
plt.plot(range(1, 13), p3, label = 'p(H0:MEAN(df_m) = MEAN(df_exm),equal_var = False))')
plt.plot(range(1, 13), [0.05 for _ in range(1, 13)], label = 'p = 0.05', lw = 0.5)
plt.plot(range(1, 13), [0.10 for _ in range(1, 13)], label = 'p = 0.05', lw = 0.5)
plt.legend()
plt.show()
上一篇下一篇

猜你喜欢

热点阅读