研究目的
本文参考东吴证券研报《A股市场的周内效应》内容,对A股市场的日历效应在周内表现进行探索。日历效应,是一个鱼龙混杂的题目。有诙谐逗笑的,如马克吐温老先生的段子:十月,这是股市最危险的月份,其他危险的月份有七月、一月、九月、四月、十一月、五月、三月、六月、十二月、八月和二月。有认真总结的,如华尔街那个著名谚语:Sell in May and go away。然而从量化的角度来讲由于其样本数量过小,逻辑支撑也不够直接,对于以月份为支撑的日历效应一直被人持以怀疑的态度。相对地,以周为时间单位的周内效应更多地成为了我们关注的对象。我们考察了时间周期内股票在周一至周五每一天涨跌幅的中位数以观察周内效应的存在性。
研究思路
【1】观察周内不同交易日指数等效组合收益分解前后的涨跌幅中位数趋势变化。
【2】观察周内不同交易日全A股收益分解前后的涨跌幅中位数趋势变化。
【3】观察周内不同交易日收益分解前后的股指期货涨跌幅变化分析异象原因。
【4】尝试构建基于周内异象的策略获取来自异象的超额收益
【4】尝试构建不同半仓t 0策略获取观察到的日内收益。
研究结论
【1】周内异象在我国A股市场内广泛存在,且该异象主要来源于日内收益,日间收益并无明显规律。
【2】周内异象的存在原因可能与资金流转、股票的t 1机制有关。
【3】在考虑了手续费以及印花税后,操作频率较低的基础策略有着明显的超额收益,而操作频率较高的以获得日内收益为目标的t 0策略超额收益会被几乎抹平,在两种情况下,周内异象的考量均可以明显改善策略绩效。
本文参考东吴证券研报《A股市场的周内效应》内容,对A股市场的周内异象进行探索。日历效应,是一个鱼龙混杂的题目。有诙谐逗笑的,如马克吐温老先生的段子:十月,这是股市最危险的月份,其他危险的月份有七月、一月、九月、四月、十一月、五月、三月、六月、十二月、八月和二月。有认真总结的,如华尔街那个著名谚语:Sell in May and go away。然而从量化的角度来讲由于其样本数量过小,逻辑支撑也不够直接,对于以月份为支撑的日历效应一直被人持以怀疑的态度。相对地,以周为时间单位的周内效应更多地成为了我们关注的对象。我们考察了时间周期内股票在周一至周五每一天涨跌幅的中位数以观察周内效应的存在性。
【1】观察周内不同交易日指数等效组合收益分解前后的涨跌幅中位数趋势变化。
【2】观察周内不同交易日全A股收益分解前后的涨跌幅中位数趋势变化。
【3】观察周内不同交易日收益分解前后的股指期货涨跌幅变化分析异象原因。
【4】尝试构建基于周内异象的策略获取来自异象的超额收益
【4】尝试构建不同半仓t+0策略获取观察到的日内收益。
【1】周内异象在我国A股市场内广泛存在,且该异象主要来源于日内收益,日间收益并无明显规律。
【2】周内异象的存在原因可能与股票的t+1机制有关。
【3】在考虑了手续费以及印花税后,操作频率较低的基础策略有着明显的超额收益,而操作频率较高的以获得日内收益为目标的t+0策略超额收益会被几乎抹平,在两种情况下,周内异象的考量均可以明显改善策略绩效。
2007.1 ~ 2019.6
from jqdata import *
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
#交易日获取函数
def get_trade_day_list(start_date, end_date):
date_df = get_price('000001.XSHG', start_date = start_date, end_date = end_date)
date_list = []
for item in list(date_df.index):
item = str(item)
y = int(item[0:4])
m = int(item[5:7])
d = int(item[8:10])
date_list.append(datetime.date(y,m,d))
return date_list
trade_day_list = get_trade_day_list(datetime.date(2007,1,16),datetime.date(2019,6,14))
首先,我们将收益区分为了日内收益(一天之中开盘价到收盘价的收益),日间收益(前一天收盘价至当天开盘价之间的收益)与总收益,并计算了2007年1月至29019年6月上证50,沪深300以及中证500三个指数等效组合对应的涨跌幅中位数,观察其在一周不同交易日内的变化趋势,发现周内的涨跌幅呈现出了强烈的“V”型趋势:从周一开始涨跌幅逐渐下降,到周日跌至低点,周五则出现了轻微的反弹,在分解收益后,我们发现这一现象主要来自日内收益,日间收益在周二至周五的几天间并没出现明显的规律。
#定义股票列表涨跌幅获取函数(全部收益)
def median_return(trade_day_list, security_list):
median_df = pd.DataFrame()
for security in security_list:
start = get_security_info(security).start_date
if start < trade_day_list[0]:
start = trade_day_list[0]
median_list = []
mon = []
tue = []
wed = []
thu = []
fri = []
price_df = get_price(security, start_date = start, end_date = trade_day_list[-1], fields = ['open', 'close'])
date_list = []
for item in list(price_df.index):
item = str(item)
y = int(item[0:4])
m = int(item[5:7])
d = int(item[8:10])
date_list.append(datetime.date(y,m,d))
for day in date_list[1:]:
location = date_list.index(day)
last_day = date_list[location - 1]
delta_price = (price_df.loc[day, 'close'] / price_df.loc[last_day, 'close']) - 1
weekday = day.weekday()
if weekday == 0:
mon.append(delta_price)
elif weekday == 1:
tue.append(delta_price)
elif weekday == 2:
wed.append(delta_price)
elif weekday == 3:
thu.append(delta_price)
elif weekday == 4:
fri.append(delta_price)
for item in [mon, tue, wed, thu, fri]:
item_df = pd.DataFrame()
item_df[str(item)] = item
item_df = item_df.dropna()
item_list = list(item_df[str(item)])
median_list.append(np.median(item_list))
median_df[security] = median_list
median_df = median_df.T
median_df.columns = ['mon', 'tue', 'wed', 'thu', 'fri']
return median_df
#定义股票列表涨跌幅获取函数(日内收益)
def median_return_inday(trade_day_list, security_list):
median_df = pd.DataFrame()
for security in security_list:
start = get_security_info(security).start_date
if start < trade_day_list[0]:
start = trade_day_list[0]
median_list = []
mon = []
tue = []
wed = []
thu = []
fri = []
price_df = get_price(security, start_date = start, end_date = trade_day_list[-1], fields = ['open', 'close'])
date_list = []
for item in list(price_df.index):
item = str(item)
y = int(item[0:4])
m = int(item[5:7])
d = int(item[8:10])
date_list.append(datetime.date(y,m,d))
for day in date_list:
delta_price = (price_df.loc[day, 'close'] / price_df.loc[day, 'open']) - 1
weekday = day.weekday()
if weekday == 0:
mon.append(delta_price)
elif weekday == 1:
tue.append(delta_price)
elif weekday == 2:
wed.append(delta_price)
elif weekday == 3:
thu.append(delta_price)
elif weekday == 4:
fri.append(delta_price)
for item in [mon, tue, wed, thu, fri]:
item_df = pd.DataFrame()
item_df[str(item)] = item
item_df = item_df.dropna()
item_list = list(item_df[str(item)])
median_list.append(np.median(item_list))
median_df[security] = median_list
median_df = median_df.T
median_df.columns = ['mon', 'tue', 'wed', 'thu', 'fri']
return median_df
#定义股票列表涨跌幅获取函数(日间收益)
def median_return_overnight(trade_day_list, security_list):
median_df = pd.DataFrame()
for security in security_list:
start = get_security_info(security).start_date
if start < trade_day_list[0]:
start = trade_day_list[0]
median_list = []
mon = []
tue = []
wed = []
thu = []
fri = []
price_df = get_price(security, start_date = start, end_date = trade_day_list[-1], fields = ['open', 'close'])
date_list = []
for item in list(price_df.index):
item = str(item)
y = int(item[0:4])
m = int(item[5:7])
d = int(item[8:10])
date_list.append(datetime.date(y,m,d))
for day in date_list[1:]:
location = date_list.index(day)
last_day = date_list[location - 1]
delta_price = (price_df.loc[day, 'open'] / price_df.loc[last_day, 'close']) - 1
weekday = day.weekday()
if weekday == 0:
mon.append(delta_price)
elif weekday == 1:
tue.append(delta_price)
elif weekday == 2:
wed.append(delta_price)
elif weekday == 3:
thu.append(delta_price)
elif weekday == 4:
fri.append(delta_price)
for item in [mon, tue, wed, thu, fri]:
item_df = pd.DataFrame()
item_df[str(item)] = item
item_df = item_df.dropna()
item_list = list(item_df[str(item)])
median_list.append(np.median(item_list))
median_df[security] = median_list
median_df = median_df.T
median_df.columns = ['mon', 'tue', 'wed', 'thu', 'fri']
return median_df
#上证50,沪深300,中证500总涨跌幅中位数
market_median = median_return(trade_day_list, ['000016.XSHG', '000300.XSHG', '000905.XSHG'])
market_median.T.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f780ede0a58>
#上证50,沪深300,中证500日内涨跌幅中位数
market_median_inday = median_return_inday(trade_day_list, ['000016.XSHG', '000300.XSHG', '000905.XSHG'])
market_median_inday.T.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f780f62ce80>
#上证50,沪深300,中证500日间涨跌幅中位数
market_median_overnight = median_return_overnight(trade_day_list, ['000016.XSHG', '000300.XSHG', '000905.XSHG'])
market_median_overnight.T.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f780e256128>
为了进一步观察现象的存在性,我们将研究扩展到了全部A股,以下是全部A股在一周不同交易日下的涨跌幅变化趋势,观察图表可知,与指数相同的周内异象在全A股的范围内依然显著存在,从周一起涨跌幅逐渐下降,跌至周四达到了最低点,在周五有着一定程度上的回升。
#定义股票列表总涨跌幅获取函数
def all_median_return(trade_day_list, security_list):
median_df = pd.DataFrame()
median_list = []
mon = []
tue = []
wed = []
thu = []
fri = []
for security in security_list:
start = get_security_info(security).start_date
if start < trade_day_list[0]:
start = trade_day_list[0]
price_df = get_price(security, start_date = start, end_date = trade_day_list[-1], fields = ['open', 'close'])
date_list = []
for item in list(price_df.index):
item = str(item)
y = int(item[0:4])
m = int(item[5:7])
d = int(item[8:10])
date_list.append(datetime.date(y,m,d))
for day in date_list[1:]:
location = date_list.index(day)
last_day = date_list[location - 1]
delta_price = (price_df.loc[day, 'close'] / price_df.loc[last_day, 'close']) - 1
weekday = day.weekday()
if weekday == 0:
mon.append(delta_price)
elif weekday == 1:
tue.append(delta_price)
elif weekday == 2:
wed.append(delta_price)
elif weekday == 3:
thu.append(delta_price)
elif weekday == 4:
fri.append(delta_price)
for item in [mon, tue, wed, thu, fri]:
item_df = pd.DataFrame()
item_df[str(item)] = item
item_df = item_df.dropna()
item_df = item_df[item_df[str(item)] != 0]
item_list = list(item_df[str(item)])
median_list.append(np.median(item_list))
median_df['all_listed_stock'] = median_list
median_df = median_df.T
median_df.columns = ['mon', 'tue', 'wed', 'thu', 'fri']
return median_df
#定义股票列表日内涨跌幅获取函数
def all_median_return_inday(trade_day_list, security_list):
median_df = pd.DataFrame()
median_list = []
mon = []
tue = []
wed = []
thu = []
fri = []
for security in security_list:
start = get_security_info(security).start_date
if start < trade_day_list[0]:
start = trade_day_list[0]
price_df = get_price(security, start_date = start, end_date = trade_day_list[-1], fields = ['open', 'close'])
date_list = []
for item in list(price_df.index):
item = str(item)
y = int(item[0:4])
m = int(item[5:7])
d = int(item[8:10])
date_list.append(datetime.date(y,m,d))
for day in date_list:
delta_price = (price_df.loc[day, 'close'] / price_df.loc[day, 'open']) - 1
weekday = day.weekday()
if weekday == 0:
mon.append(delta_price)
elif weekday == 1:
tue.append(delta_price)
elif weekday == 2:
wed.append(delta_price)
elif weekday == 3:
thu.append(delta_price)
elif weekday == 4:
fri.append(delta_price)
for item in [mon, tue, wed, thu, fri]:
item_df = pd.DataFrame()
item_df[str(item)] = item
item_df = item_df.dropna()
item_df = item_df[item_df[str(item)] != 0]
item_list = list(item_df[str(item)])
median_list.append(np.median(item_list))
median_df['all_listed_stock'] = median_list
median_df = median_df.T
median_df.columns = ['mon', 'tue', 'wed', 'thu', 'fri']
return median_df
#定义股票列表日间涨跌幅获取函数
def all_median_return_overnight(trade_day_list, security_list):
median_df = pd.DataFrame()
median_list = []
mon = []
tue = []
wed = []
thu = []
fri = []
for security in security_list:
start = get_security_info(security).start_date
if start < trade_day_list[0]:
start = trade_day_list[0]
price_df = get_price(security, start_date = start, end_date = trade_day_list[-1], fields = ['open', 'close'])
date_list = []
for item in list(price_df.index):
item = str(item)
y = int(item[0:4])
m = int(item[5:7])
d = int(item[8:10])
date_list.append(datetime.date(y,m,d))
for day in date_list:
location = date_list.index(day)
last_day = date_list[location - 1]
delta_price = (price_df.loc[day, 'open'] / price_df.loc[last_day, 'close']) - 1
weekday = day.weekday()
if weekday == 0:
mon.append(delta_price)
elif weekday == 1:
tue.append(delta_price)
elif weekday == 2:
wed.append(delta_price)
elif weekday == 3:
thu.append(delta_price)
elif weekday == 4:
fri.append(delta_price)
for item in [mon, tue, wed, thu, fri]:
item_df = pd.DataFrame()
item_df[str(item)] = item
item_df = item_df.dropna()
item_df = item_df[item_df[str(item)] != 0]
item_list = list(item_df[str(item)])
median_list.append(np.median(item_list))
median_df['all_listed_stock'] = median_list
median_df = median_df.T
median_df.columns = ['mon', 'tue', 'wed', 'thu', 'fri']
return median_df
#全A总涨跌幅中位数
all_stock_list = get_index_stocks('000985.XSHG')
all_median = all_median_return(trade_day_list, all_stock_list)
all_median.plot(kind = 'bar', figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f77b68e0e48>
#全A日内涨跌幅中位数
all_stock_list = get_index_stocks('000985.XSHG')
all_median_inday = all_median_return_inday(trade_day_list, all_stock_list)
all_median_inday.plot(kind = 'bar', figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f77d9f56940>
#全A日间涨跌幅中位数
all_stock_list = get_index_stocks('000985.XSHG')
all_median_overnight = all_median_return_overnight(trade_day_list, all_stock_list)
all_median_overnight.plot(kind = 'bar', figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f77d0eba160>
对于周内异象的形成原因,我们猜测与A股的t+1交易机制有关:周五卖出的股票,需等到下周一,才能通过银证转账取得现金;周末需要取用的现金,必须提早至周四将股票卖出。因此,周四的股票交易中,会存在一个额外的卖压,容易成为一周中行情最低迷的一天。
为了进一步验证这一结论,我们将没有t+0交易限制的期货市场行情数据加以同样的处理方式。观察图表可以发现,期指市场并未像A股市场一样出现显著的周内效应,进一步验证了我们的结论:周内异象的形成与A股的t+1交易机制有关。
#上证50,沪深300,中证500股指期货涨跌幅中位数
futures_market_median = median_return(trade_day_list, ['IH9999.CCFX', 'IF9999.CCFX', 'IC9999.CCFX'])
futures_market_median.T.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f89771c8588>
通过前文的研究我们可以发现,在我国A股市场中周一、周二、周三、周五平均来讲存在正向收益,而正向收益的来源基本为日内收益,因此我们将研究策略主要分为直接获取周内收益以及通过半仓t+0获取日内收益两种,我们首先着眼于直接获得周内收益的基础策略,即周五开盘时买入,持有至周三收盘时卖出。
我们测试了上证50、沪深300以及中证500的等效组合收益情况。通过测试我们可以发现,由于较低的操作频率,在规避掉周四以后三种组合均获得了明显的超额收益,优于周内收益主要来自于日内而日间收益则基本为负,接下来我们将观察基于半仓t+0策略仅获取日间收益的可行性。
#基础周内策略——周五买入周三卖出
#(买入时万分之三,卖出时万分之三加千分之一印花税)
def weekly_value_cal(security, trade_day_list, fee_rate = 0.0003, tax_rate = 0.001):
value_df = pd.DataFrame()
price_df = get_price(security, start_date = trade_day_list[0], end_date = trade_day_list[-1], fields = ['open', 'close'])
price_df = price_df.dropna()
value_df['benchmark'] = price_df['close'] / price_df.loc[price_df.index[0], 'close']
date_list = list(value_df.index)
if date_list[0].weekday() == 3:
date_list = date_list.remove(date_list[0])
value_df = value_df.drop([date_list[0]])
value_list = [(1 - fee_rate) * price_df.loc[date_list[0], 'close']]
for date in date_list[1:]:
weekday = date.weekday()
location = date_list.index(date)
last_date = date_list[location - 1]
if weekday == 0 or weekday == 1:
value = value_list[location - 1] * price_df.loc[date, 'close'] / price_df.loc[last_date, 'close']
value_list.append(value)
elif weekday == 2:
value = value_list[location - 1] * price_df.loc[date, 'close'] * (1 - fee_rate - tax_rate) / price_df.loc[last_date, 'close']
value_list.append(value)
elif weekday == 3:
value = value_list[location - 1]
value_list.append(value)
elif weekday == 4:
value = value_list[location - 1] * (1 - fee_rate) * price_df.loc[date, 'close'] / price_df.loc[date, 'open']
value_list.append(value)
value_df['value'] = value_list
value_df['value'] = value_df['value'] / value_list[0]
return value_df
#上证50指数等效组合价值曲线(基础)
SZ50_value_weekly = weekly_value_cal('000016.XSHG', trade_day_list)
SZ50_value_weekly['zero_cost'] = weekly_value_cal('000016.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
SZ50_value_weekly['fee'] = weekly_value_cal('000016.XSHG', trade_day_list, tax_rate = 0)['value']
SZ50_value_weekly['tax'] = weekly_value_cal('000016.XSHG', trade_day_list, fee_rate = 0)['value']
SZ50_value_weekly.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f780a804cc0>
#上证50指数等效组合价值曲线(基础)
SZ50_value_weekly = weekly_value_cal('000016.XSHG', trade_day_list)
SZ50_value_weekly['zero_cost'] = weekly_value_cal('000016.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
SZ50_value_weekly['fee'] = weekly_value_cal('000016.XSHG', trade_day_list, tax_rate = 0)['value']
SZ50_value_weekly['tax'] = weekly_value_cal('000016.XSHG', trade_day_list, fee_rate = 0)['value']
SZ50_value_weekly.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f780a804cc0>
#沪深300指数等效组合价值曲线(基础)
HS300_value_weekly = weekly_value_cal('000300.XSHG', trade_day_list)
HS300_value_weekly['zero_cost'] = weekly_value_cal('000300.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
HS300_value_weekly['fee'] = weekly_value_cal('000300.XSHG', trade_day_list, tax_rate = 0)['value']
HS300_value_weekly['tax'] = weekly_value_cal('000300.XSHG', trade_day_list, fee_rate = 0)['value']
HS300_value_weekly.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f77d1513f28>
#中证500指数等效组合价值曲线(基础)
ZZ500_value_weekly = weekly_value_cal('000905.XSHG', trade_day_list)
ZZ500_value_weekly['zero_cost'] = weekly_value_cal('000905.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
ZZ500_value_weekly['fee'] = weekly_value_cal('000905.XSHG', trade_day_list, tax_rate = 0)['value']
ZZ500_value_weekly['tax'] = weekly_value_cal('000905.XSHG', trade_day_list, fee_rate = 0)['value']
ZZ500_value_weekly.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f780a6cc400>
获取A股日内收益的方法最直接的是每日开盘买入、收盘卖出,但是由于t+1交易机制的限制,当日买入的股票无法当日卖出。因此如果想获取这部分收益,需要一部分底仓的存在。所谓半仓策略,即初始仓位里有一半现金和一半股票,每日开盘时现金购入股票,仓位变为满仓,收盘时将仓位中原有的股票卖出,回到半仓状态,如此循环往复。基础操作如下:
【1】初始净值假设为1,定义基准为证券本身。
【2】在第一日开盘时买入半仓,第二天开盘时再补至满仓,收盘时卖出一半股票,恢复半仓状态,如此循环往复。
【3】计算手续费,印花税下策略净值走势和绩效表现。
首先我们测试了在不考虑周内异象的半仓t+0策略绩效,可以发现,从指数的角度来讲,由于高昂的手续费以及印花税,本来可观的日内收益收益被完全抹平,其数值低于基准收益很多。接下来,我们将通过两种不同方式从周内异象对层面上尝试对半仓t+0策略进行优化。
#不考虑周内异象获取价值数据
#(买入时万分之三,卖出时万分之三加千分之一印花税)
def basic_value_cal(security, trade_day_list, fee_rate = 0.0003, tax_rate = 0.001):
value_df = pd.DataFrame()
price_df = get_price(security, start_date = trade_day_list[0], end_date = trade_day_list[-1], fields = ['open', 'close'])
price_df = price_df.dropna()
value_df['benchmark'] = price_df['close'] / price_df.loc[price_df.index[0], 'close']
date_list = list(value_df.index)
value_list_1 = [(1- fee_rate - tax_rate) * price_df.loc[price_df.index[0], 'close']]
value_list_2 = [price_df.loc[price_df.index[0], 'open']]
a = 1
value_list = [value_list_1[0] + value_list_2[0]]
for date in date_list[1:]:
location = date_list.index(date)
last_date = date_list[location - 1]
if a == 1:
value_1 = value_list_1[location - 1] * price_df.loc[date, 'close'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * price_df.loc[date, 'close'] * (1 - fee_rate) / price_df.loc[date, 'open']
value_list_2.append(value_2)
a = 0
else:
value_1 = value_list_1[location - 1] * price_df.loc[date, 'close'] * (1 - fee_rate) / price_df.loc[date, 'open']
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * price_df.loc[date, 'close'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_2.append(value_2)
a = 1
value_list.append(value_1 + value_2)
# value_df['value_1'] = value_list_1
# value_df['value_1'] = value_df['value_1'] / value_list_1[0]
# value_df['value_2'] = value_list_2
# value_df['value_2'] = value_df['value_2'] / value_list_2[0]
value_df['value'] = value_list
value_df['value'] = value_df['value'] / value_list[0]
return value_df
#上证50指数等效组合价值曲线(基础)
SZ50_value_basic = basic_value_cal('000016.XSHG', trade_day_list)
SZ50_value_basic['zero_cost'] = basic_value_cal('000016.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
SZ50_value_basic['fee'] = basic_value_cal('000016.XSHG', trade_day_list, tax_rate = 0)['value']
SZ50_value_basic['tax'] = basic_value_cal('000016.XSHG', trade_day_list, fee_rate = 0)['value']
SZ50_value_basic.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7fe63e464e80>
#沪深300指数等效组合价值曲线(基础)
HS300_value_basic = basic_value_cal('000300.XSHG', trade_day_list)
HS300_value_basic['zero_cost'] = basic_value_cal('000300.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
HS300_value_basic['fee'] = basic_value_cal('000300.XSHG', trade_day_list, tax_rate = 0)['value']
HS300_value_basic['tax'] = basic_value_cal('000300.XSHG', trade_day_list, fee_rate = 0)['value']
HS300_value_basic.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7fe63e268f98>
#中证500指数等效组合价值曲线(基础)
ZZ500_value_basic = basic_value_cal('000905.XSHG', trade_day_list)
ZZ500_value_basic['zero_cost'] = basic_value_cal('000905.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
ZZ500_value_basic['fee'] = basic_value_cal('000905.XSHG', trade_day_list, tax_rate = 0)['value']
ZZ500_value_basic['tax'] = basic_value_cal('000905.XSHG', trade_day_list, fee_rate = 0)['value']
ZZ500_value_basic.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7fe63e58d320>
通过基础半仓t+0策略的构建过程我们可以发现其获取的是每一天的日内收益,但由于我们的研究发现,周四的日内收益显著为负,因此对于基础的半仓t+0策略,我们增加的改进方式如下:
【1】在策略执行到周四时不进行买卖操作。
【2】在周三开盘时买入的那部分股票在周五收盘时卖出。
【3】在周三收盘时卖出的资金在周五开盘时重新买入。
在这样改进后观察图表,我们可以发现尽管高昂的手续费依然会抹平日内收益,但修正后的价值曲线相对于基础价值曲线有了显著的整体上移,特别地,在中证500的情况下策略曲线与基准几乎重合,大幅度地提高了半仓t+0策略的绩效。
#半仓t+0策略获取价值数据且直接跳过周四
#(买入时万分之三,卖出时万分之三加千分之一印花税)
def value_cal(security, trade_day_list, fee_rate = 0.0003, tax_rate = 0.001):
value_df = pd.DataFrame()
price_df = get_price(security, start_date = trade_day_list[0], end_date = trade_day_list[-1], fields = ['open', 'close'])
price_df = price_df.dropna()
value_df['benchmark'] = price_df['close'] / price_df.loc[price_df.index[0], 'close']
date_list = list(value_df.index)
value_list_1 = [(1- fee_rate) * price_df.loc[price_df.index[0], 'close']]
value_list_2 = [price_df.loc[price_df.index[0], 'open']]
a = 1
value_list = [value_list_1[0] + value_list_2[0]]
for date in date_list[1:]:
location = date_list.index(date)
last_date = date_list[location - 1]
weekday = date.weekday()
if weekday == 3:
date_list.remove(date)
value_df = value_df.drop([date])
continue
if a == 1:
value_1 = value_list_1[location - 1] * price_df.loc[date, 'close'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * price_df.loc[date, 'close'] * (1 - fee_rate) / price_df.loc[date, 'open']
value_list_2.append(value_2)
a = 0
else:
value_1 = value_list_1[location - 1] * price_df.loc[date, 'close'] * (1 - fee_rate) / price_df.loc[date, 'open']
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * price_df.loc[date, 'close'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_2.append(value_2)
a = 1
value_list.append(value_1 + value_2)
# value_df['value_1'] = value_list_1
# value_df['value_1'] = value_df['value_1'] / value_list_1[0]
# value_df['value_2'] = value_list_2
# value_df['value_2'] = value_df['value_2'] / value_list_2[0]
value_df['value'] = value_list
value_df['value'] = value_df['value'] / value_list[0]
return value_df
#上证50指数等效组合价值曲线
SZ50_value = value_cal('000016.XSHG', trade_day_list)
SZ50_value['zero_cost'] = value_cal('000016.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
SZ50_value['fee'] = value_cal('000016.XSHG', trade_day_list, tax_rate = 0)['value']
SZ50_value['tax'] = value_cal('000016.XSHG', trade_day_list, fee_rate = 0)['value']
SZ50_value.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f19d7456d30>
#沪深300指数等效组合价值曲线
HS300_value = value_cal('000300.XSHG', trade_day_list)
HS300_value['zero_cost'] = value_cal('000300.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
HS300_value['fee'] = value_cal('000300.XSHG', trade_day_list, tax_rate = 0)['value']
HS300_value['tax'] = value_cal('000300.XSHG', trade_day_list, fee_rate = 0)['value']
HS300_value.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f19d7233be0>
#中证500指数等效组合价值曲线
ZZ500_value = value_cal('000905.XSHG', trade_day_list)
ZZ500_value['zero_cost'] = value_cal('000905.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
ZZ500_value['fee'] = value_cal('000905.XSHG', trade_day_list, tax_rate = 0)['value']
ZZ500_value['tax'] = value_cal('000905.XSHG', trade_day_list, fee_rate = 0)['value']
ZZ500_value.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f19d71cccc0>
与上文相似地,对于周四显著为负的日内收益我们还有另一种调整方式,即对于每个周四的买卖方式进行改变:
【1】对于周三收盘时卖出的资金,在周四收盘时进行买入。
【2】对于周三在开盘时买入的股票,在周四开盘时进行卖出。
这种改进方式可以在不影响周三以及周五仓位的情况下规避掉周四的负日内收益,观察图表可以发现,这一操作带来的效果与上一种操作程度上相似,也可以显著地提高半仓t+0策略的绩效。
综上所著,周内异象可以应用于多种形式的A股组合收益的提升,且由于我国股市高昂的交易成本,仅以获得日内收益为目标的t+0策略表现并没有交易频率较低的基础策略好。交易成本与超额收益的均衡在我国A股市场的投资中至关重要。
#半仓t+0策略获取价值数据且周四时早上卖出半仓晚上买入半仓
#(买入时万分之三,卖出时万分之三加千分之一印花税)
def thu_value_cal(security, trade_day_list, fee_rate = 0.0003, tax_rate = 0.001):
value_df = pd.DataFrame()
price_df = get_price(security, start_date = trade_day_list[0], end_date = trade_day_list[-1], fields = ['open', 'close'])
price_df = price_df.dropna()
value_df['benchmark'] = price_df['close'] / price_df.loc[price_df.index[0], 'close']
date_list = list(value_df.index)
value_list_1 = [(1- fee_rate) * price_df.loc[price_df.index[0], 'close']]
value_list_2 = [price_df.loc[price_df.index[0], 'open']]
a = 1
value_list = [value_list_1[0] + value_list_2[0]]
for date in date_list[1:]:
location = date_list.index(date)
last_date = date_list[location - 1]
weekday = date.weekday()
if weekday == 3:
if a == 1:
value_1 = value_list_1[location - 1] * price_df.loc[date, 'open'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * (1 - fee_rate)
value_list_2.append(value_2)
a = 0
else:
value_1 = value_list_1[location - 1] * (1 - fee_rate)
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * price_df.loc[date, 'open'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_2.append(value_2)
a = 1
else:
if a == 1:
value_1 = value_list_1[location - 1] * price_df.loc[date, 'close'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * price_df.loc[date, 'close'] * (1 - fee_rate) / price_df.loc[date, 'open']
value_list_2.append(value_2)
a = 0
else:
value_1 = value_list_1[location - 1] * price_df.loc[date, 'close'] * (1 - fee_rate) / price_df.loc[date, 'open']
value_list_1.append(value_1)
value_2 = value_list_2[location - 1] * price_df.loc[date, 'close'] * (1 - tax_rate - fee_rate) / price_df.loc[last_date, 'close']
value_list_2.append(value_2)
a = 1
value_list.append(value_1 + value_2)
# value_df['value_1'] = value_list_1
# value_df['value_1'] = value_df['value_1'] / value_list_1[0]
# value_df['value_2'] = value_list_2
# value_df['value_2'] = value_df['value_2'] / value_list_2[0]
value_df['value'] = value_list
value_df['value'] = value_df['value'] / value_list[0]
return value_df
#上证50指数等效组合价值曲线(含周四)
SZ50_value_thu = thu_value_cal('000016.XSHG', trade_day_list)
SZ50_value_thu['zero_cost'] = thu_value_cal('000016.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
SZ50_value_thu['fee'] = thu_value_cal('000016.XSHG', trade_day_list, tax_rate = 0)['value']
SZ50_value_thu['tax'] = thu_value_cal('000016.XSHG', trade_day_list, fee_rate = 0)['value']
SZ50_value_thu.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f19d74f0978>
#沪深300指数等效组合价值曲线(含周四)
HS300_value_thu = thu_value_cal('000300.XSHG', trade_day_list)
HS300_value_thu['zero_cost'] = thu_value_cal('000300.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
HS300_value_thu['fee'] = thu_value_cal('000300.XSHG', trade_day_list, tax_rate = 0)['value']
HS300_value_thu['tax'] = thu_value_cal('000300.XSHG', trade_day_list, fee_rate = 0)['value']
HS300_value_thu.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f19d74319e8>
#中证500指数等效组合价值曲线(含周四)
ZZ500_value_thu = thu_value_cal('000905.XSHG', trade_day_list)
ZZ500_value_thu['zero_cost'] = thu_value_cal('000905.XSHG', trade_day_list, fee_rate = 0, tax_rate = 0)['value']
ZZ500_value_thu['fee'] = thu_value_cal('000905.XSHG', trade_day_list, tax_rate = 0)['value']
ZZ500_value_thu['tax'] = thu_value_cal('000905.XSHG', trade_day_list, fee_rate = 0)['value']
ZZ500_value_thu.plot(figsize = (20,12))
<matplotlib.axes._subplots.AxesSubplot at 0x7f19d73b1668>
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程