中证红利指数是一个反映A股市场高红利股票整体状况和走势的指数。它通过选取上海、深圳交易所中现金股息率高、分红比较稳定、具有一定规模及流动性的100只股票作为样本。这个指数的目的是提供一个全面且具有代表性的视角,以观察A股市场中高红利股票的表现。中证红利指数的样本选择标准包括过去两年连续现金分红且每年的税后现金股息率均大于0,以及近一年内日均流通市值排名前80%,同时考虑流动性等指标。这个指数的推出,旨在为投资者提供一个专注于高股息率股票的投资工具,同时也反映了市场对于稳定分红和高股息率股票的需求。
中证红利指数的特点包括其防御性较强和成长属性不高。由于指数中的成分股主要来自发展相对成熟的行业,因此在市场表现不佳时,这些成分股往往能够保持相对稳定的盈利,使得中证红利指数在熊市中表现出较好的抗跌性。然而,在牛市中,由于成长属性不高,中证红利指数的表现可能不如其他一些成长型指数。
此外,中证红利指数的成分股涵盖了多个行业,包括但不限于房地产、钢铁、交通运输、银行等,这些行业的配置比例会根据市场情况和选股规则的变化而调整。因此,中证红利指数不仅反映了高股息率股票的整体表现,还隐含了对特定行业趋势的跟踪。
中证红利基日为2024年12月31日,基点为1000点,指数代码为000922(上海)/399922(深圳),于2005年5月26日正式发行。
基于中证红利的指数增强基金策略的构建
需要根据不同指数的特点,构建相应的增强性策略。从前文分析看,中证红利指数选择的是沪深两市中现金股息率最高、分红稳定、具有一定规模及流动性的100只股票,其特点就是:稳定性强、业绩较好、同时具有一定的规模。但是对个股的成长性没有特别要求。
因此,我们还可以通过以下指标构建中证红利指数增强基金:
股息率:股息率(Dividend Yield Ratio),是一年的总派息额与当时市价的比例。以占股票最后销售价格的百分数表示的年度股息,该指标是投资收益率的简化形式。股息率是股息与股票价格之间的比率。在投资实践中,股息率是衡量企业是否具有投资价值的重要标尺之一。
市净率:市净率(Price-to-Book Ratio,简称P/B PBR)指的是每股股价与每股净资产的比率。 市净率可用于股票投资分析,一般来说市净率较低的股票,投资价值较高,相反,则投资价值较低;但在判断投资价值时还要考虑当时的市场环境以及公司经营情况、盈利能力等因素。
市盈率:市盈率(Price Earnings Ratio,简称P/E或PER),也称“本益比”、“股价收益比率”或“市价盈利比率(简称市盈率)”。市盈率是指股票价格除以每股收益(每股收益,EPS)的比率。或以公司市值除以年度股东应占溢利。计算时,股价通常取最新收盘价,而EPS方面,若按已公布的上年度EPS计算,称为历史市盈率(historical P/E);计算预估市盈率所用的EPS预估值,一般采用市场平均预估(consensus estimates),即追踪公司业绩的机构收集多位分析师的预测所得到的预估平均值或中值。何谓合理的市盈率没有一定的准则。
贝塔值:与大盘的联动波动率;
自相关波动率:自身的波动率;
构建策略:
根据股息率、市盈率、市净率、beta值、波动率对中证红利的成分股进行等权重排序(我们需要选择股息率高的,市盈率/市净率/bera值/波动率都比较低的股票作为标的股票,因子这里的权重值设计为,股息率为1,其他的权重值都为-1),之后选择排名前5名的股票进行买入操作,每月循环一次,看一下最终的收益如何。
新建一个py文件,作为中证红利增强型的函数文件,主要作用是根据以上条件获取排序后的股票清单,我们这里命名为:bouns_enhance.py 代码如下:
import numpy as np
import pandas as pd
from gm.api import *
import datetime
import statsmodels.api as sm
from dateutil.relativedelta import relativedelta
from sklearn.preprocessing import MinMaxScaler
"""
根据股息率/市盈率/市净率/beta值/波动率等权重合成因子,对中证红利指数的成分股进行排序
"""
def bouns_enhance(now,index = "SHSE.000922"):
last_day = get_previous_trading_date("SHSE",now)
symbol_dir = stk_get_index_constituents(index) # 获取成分股数据
symbol_list = symbol_dir["symbol"].values.tolist()
DY_PE_PB = stk_get_daily_valuation_pt(symbols=symbol_list, fields="dy_lfy,pe_ttm,pb_lyr", trade_date=last_day, df=True) # 获取股息率/市盈率/市净率
DY_PE_PB = DY_PE_PB.dropna() # dataframe中已包含了股息率/市盈率/市净率数据
symbol_list = []
for symbol in DY_PE_PB["symbol"].values : #提取剔除无效数据后的股票序列
symbol_list.append(symbol)
beta_list = []
volatility_list = []
for symbol in symbol_list:
beta = get_beta_weight(symbol,now,count=20) #获取波动率数据
beta_list.append(beta)
volatility = get_volatility_normal(symbol,now,count=20)
volatility_list.append(volatility)
DY_PE_PB["BETA"] = beta_list
DY_PE_PB["volatility"] = volatility_list
#计算各个因子的相关系数,以便于设置权重,这个后续在写,目前按照等权重预估
# 对各个因子进行加权处理
factor_matrix = DY_PE_PB.iloc[:,2:] #提取因子
factor_matrix = np.asmatrix(factor_matrix)
weight_matrix = np.asmatrix([[1],[-1],[-1],[-1],[-1]]) #设置各个因子权重
res = np.dot(factor_matrix,weight_matrix) #对因子和权重进行点乘
DY_PE_PB["SCORE"] = res
DY_PE_PB = DY_PE_PB.sort_values(["SCORE"],ascending=False) #根据计算结果进行从大到小排序
symbol_list = DY_PE_PB['symbol'].values
return symbol_list
"""
beta系数:与大盘联动的波动率;
一般用单个股票的历史收益率对同期大盘指数收益率进行回归,回归系数就是BETA系数
"""
def get_beta_weight(symbol,now,count,market_index="SHSE.000001"):
last_day = get_previous_trading_date("SHSE",now)
market_data = history_n(symbol=market_index, frequency="1d", count=count, adjust=ADJUST_PREV,end_time=last_day, fields="high,low,close",df=True)
market_close = market_data["close"].values.tolist()
market_close_ratio = []
for i in range(1,len(market_close)):
ratio = (market_close[i]-market_close[i-1])/market_close[i-1]#获取大盘每天的收益率
market_close_ratio.append(ratio)
symbol_data = history_n(symbol=symbol, frequency="1d", count=count, adjust=ADJUST_PREV,end_time=last_day, fields="high,low,close",df=True)
symbol_close= symbol_data["close"].values.tolist()
symbol_close_ratio = []
for i in range(1,len(symbol_close)):
ratio = (symbol_close[i]-symbol_close[i-1])/symbol_close[i-1] #获取目标股票每天的收益率
symbol_close_ratio.append(ratio)
# 有可能数据有缺失,因此需要判断是否相等,对于缺失数据,波动率设置为无限大的值
if len(market_close_ratio) == len(symbol_close_ratio):
market_close_ratio = sm.add_constant(market_close_ratio) # 因为OLS回归中不包括常数项,因此在数据左侧增加上常数项
model = sm.OLS(symbol_close_ratio, market_close_ratio) # 做线上拟合
results = model.fit() # 然后获取拟合结果
beta_weight = results.params[1] # 拟合公式为kx+b的形式,提取拟合后的k值
else:
beta_weight = 100
return beta_weight
"""
自相关波动率:自身的波动率;
"""
def get_volatility_normal(symbol,now,count):
last_day = get_previous_trading_date("SHSE",now)
symbol_data = history_n(symbol=symbol, frequency="1d", count=count, adjust=ADJUST_PREV,end_time=last_day, fields="high,low,close",df=True)
symbol_data_close = symbol_data["close"].values.tolist()
res = np.var(symbol_data_close)/np.mean(symbol_data_close) #var() 函数通常指的是 numpy 库中的 numpy.var() 函数,它用来计算给定数组(或一维数组的某轴向)的样本方差或者总体方差,取决于提供的参数。
res = np.sqrt(res)
return res
"""
测试代码;
"""
set_token("自己的token码")
index = "SHSE.000922"
now = datetime.datetime.now()
symbol_list = bouns_enhance(now,index)
print(symbol_list)
# beta = get_beta_weight("SHSE.600501",now,count=20)
# print(beta)
之后我们通过主函数调用上面的函数,进行回测,这里我们建立一个新的py文件,命名为bouns_enhance_main.py,代码如下:
# coding=utf-8
from __future__ import print_function, absolute_import
import os
import numpy as np
import pandas as pd
from gm.api import *
import datetime
import statsmodels.api as sm
from dateutil.relativedelta import relativedelta
from sklearn.preprocessing import MinMaxScaler
import bouns_enhance as be
def init(context):
# 在init函数中设置全局变量
# algo执行定时任务函数,只能传context参数
# index股票池代码
# num买卖股票数据,暂定5只
# schedule在指定时间自动执行策略算法, 通常用于选股类型策略,每月一次,早上9点31分执行定时任务
# date_rule执行频率,目前暂时支持1d、1w、1m,其中1w、1m仅用于回测,实时模式1d以上的频率,需要在algo判断日期
# time_rule执行时间, 注意多个定时任务设置同一个时间点,前面的定时任务会被后面的覆盖
context.index = "SHSE.000922"
context.num = 5
schedule(schedule_func=algo, date_rule='1m', time_rule='09:31:00') #设置回测方式,定时任务
def algo(context):
now = context.now #获取当前时间
day_time, hour_and_mins = str(now.strftime('%Y-%m-%d %H:%M:%S')).split(" ") # Python time.strftime() 函数用于格式化时间,返回以可读字符串表示的当地时间;详细用法见 https://www.runoob.com/python/att-time-strftime.html
order_close_all() #这里为例简化,全部清仓上一阶段股票
target_list = be.bouns_enhance(now,index=context.index) #获得排序后的股票代码清单
target_symbol = target_list[:context.num] #取列表中前5只作为标的股票
for symbol in target_symbol:
order_target_percent(symbol=symbol,percent=1/context.num,order_type=OrderType_Market,position_side=PositionSide_Long) #执行买入操作
print("买入",symbol,now)
# 查看最终的回测结果
def on_backtest_finished(context, indicator):
print(indicator)
if __name__ == '__main__':
'''
strategy_id策略ID, 由系统生成
filename文件名, 请与本文件名保持一致
mode运行模式, 实时模式:MODE_LIVE回测模式:MODE_BACKTEST
token绑定计算机的ID, 可在系统设置-密钥管理中生成
backtest_start_time回测开始时间
backtest_end_time回测结束时间
backtest_adjust股票复权方式, 不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
backtest_initial_cash回测初始资金
backtest_commission_ratio回测佣金比例
backtest_slippage_ratio回测滑点比例
backtest_match_mode市价撮合模式,以下一tick/bar开盘价撮合:0,以当前tick/bar收盘价撮合:1
'''
run(strategy_id='自己的策略ID',
filename='bouns_enhance_main.py',
mode=MODE_BACKTEST,
token='自己的token码',
backtest_start_time='2021-01-01 09:00:00',
backtest_end_time='2024-05-31 15:00:00',
backtest_adjust=ADJUST_PREV,
backtest_initial_cash=100000,
backtest_commission_ratio=0.0001,
backtest_slippage_ratio=0.0001,
backtest_match_mode=1)
我们回测的时间段为2021年1月1日到2024年5月31日,回测结果如下:
从回测数据我们可以看到,目前这个增强型策略相比于中证红利的超额收益率为39.25%,并不是十分理想,我们接下来我们可以通过对不同因子的权重值进行条件,以便优化这个增强型策略。
同时我还测算了2020年1月1日到2024年5月31日,结果大不相同:
超额收益率达到了101.4%,从折线图中看,主要是2020年7月份超额收益率比较高,拉高了整体的水平,这个阶段买入的股票有:
买入 SHSE.600188 2020-07-01 09:31:00+08:00 #兖矿能源,上涨137%
买入 SHSE.601216 2020-07-01 09:31:00+08:00 #君正集团,上涨312%
买入 SZSE.002128 2020-07-01 09:31:00+08:00 #电投能源,上涨69.84%
买入 SHSE.600971 2020-07-01 09:31:00+08:00 #恒源煤电,上涨33%
买入 SHSE.600325 2020-07-01 09:31:00+08:00 #华发股份,上涨8%
几个股票的暴涨提高了整体的收益率,但是这并不能说明我们的策略是有效的,我们以后在分析策略有效性的时候,还是需要选择多个样本,多个时间段进行测算,才能得到相对准确的结果。