本节主要是针对上节讲解的进行回测:
策略:
首先根据上节所选的因子进行选股,各个因子的权重都设置为1,之后对加权后的因子进行排序,选择因子权重值大的5只股票,进行买入,每个月执行一次上述策略,看最终收益率情况如何。
首先先编写函数代码,新建一个py文件,我们这里就命名为grow_yinzi_strange.py,内部代码如下:
import numpy as np
import pandas as pd
import gm.api as gm
import datetime
from dateutil.relativedelta import relativedelta
from sklearn.preprocessing import MinMaxScaler
def min_max_scaling(data):
min_val = np.min(data)
max_val = np.max(data)
return (data - min_val) / (max_val - min_val)
def grow_yinzi(index,now):
HS300_array = gm.stk_get_index_constituents(index) #获取沪深300成分股数据
"""
按照股票代码从大到小进行排序,注意下面这句非常重要,因为在实际使用过程中我发现,gm.stk_get_finance_deriv()和参数symbols=HS300_symbol_list中的顺序
并不一样,这回导致我们采用for循环中得到的参数数据和采用gm.stk_get_finance_deriv()顺序完全不同,所以我们先对股票代码进行排序,以控制for循环中返回的参数数据
之后我们再把gm.stk_get_finance_deriv()中得到的顺序同样进行从大到小排序,才能保持两组数据完全一致。
"""
HS300_array = HS300_array.sort_values(["symbol"],ascending=False) # 按照股票代码从大到小排序
HS300_symbol_array = HS300_array['symbol'].values
HS300_symbol_list = list(HS300_symbol_array) # 转换为list类型才能进行后续处理
# 采用pd.DataFrame建立二维数据表,初始化数据表,后续可以将数据存储到表中
factor_matrix = pd.DataFrame([])
factor_matrix["symbol"] = HS300_symbol_list
# factor_matrix["earnings before interest and tax"] = -999 # 息税前收益增长率,很多数据都差不到,暂时不用这个因子
factor_matrix["net_prof_yoy"] = -999 # 净利润同比增长率
factor_matrix["oper_prof_yoy"] = -999 # 营业利润同比增长率
factor_matrix["ttl_asset_yoy"] = -999 # 总资产同比增长率
factor_matrix["net_cf_oper_yoy"] = -999 # 经营活动产生的现金流量净额同比增长率
factor_matrix["net_asset_yoy"] = -999 # 净资产同比增长率
factor_matrix["eps_bas_yoy"] = -999 # 基本每股收益同比增长率
factor_matrix["roe_yoy"] = -999 # 净资产收益率同比增长率(摊薄)
factor_matrix["ttl_prof_yoy"] = -999 # 利润总额同比增长率
"""
在这里所有的值都被定义为-999,这样做的目的是在读取数据的时候,防止有数据缺失或者出错,将默认值设置成现实数据中可能遇到的最小值。
这样做的好处是在后续计算时可以自动将出错的数据的计算结果降为最差的结果,自动排除出错的数据集
后面我们也可以这样做,先批量获取数据值,之后判断数据有无缺失,如果产生缺失,就逐个获取对应因子的数据,对于缺失的因子数据,采用-999进行填充
"""
day_time,hour_and_mins = str(now.strftime('%Y-%m-%d %H:%M:%S')).split(" ") # 调用datetime函数获取最新时间
six_months_ago = now - relativedelta(months=3) # 获取9个月前的时间作为后续查询数据的起始时间(主要是息税前收益增长率的计算需要T-1的数据,所有这里设置需要大于6个月)
last_day_time,last_hour_and_mins = str(six_months_ago.strftime('%Y-%m-%d %H:%M:%S')).split(" ") # 转换时间格式到str
# 求息税前收益增长率,这个没有现成的公式,需要手动计算,计算公式为息税前收益增长率=(本期息税前利润 – 上期息税前利润) / 上期息税前利润 × 100%
# 采用dataframe格式获取数据,因为有一些数据无法获取到,所以运行起来特别慢,先注释掉,不采用这个参数
"""
for number in range(len(HS300_symbol_list)):
try:
EBIT = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="ebit_ps", start_date=last_day_time, end_date=day_time, df=True) #这里采用的是获取单个股票的数据
now_EBIT = EBIT.loc[len(EBIT)-1,["ebit_ps"]]
last_EBIT = EBIT.loc[len(EBIT)-2,["ebit_ps"]]
EBITG = ((now_EBIT-last_EBIT)/last_EBIT)["ebit_ps"] # 计算后取列表中真正的float值,如果不增加后面的索引,对象值为object
factor_matrix.loc[number,["earnings before interest and tax"]] = EBITG
except:
factor_matrix.loc[number,["earnings before interest and tax"]] = -999
print(factor_matrix)
"""
#获取净利润同比增长率数据
NPY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list,fields="net_prof_yoy",date=day_time,df=True)
NPY = NPY.sort_values(["symbol"],ascending=False) #返回值symbol顺序已经被打乱,需要重新排序
NPY = NPY.reset_index(drop=True) # 需要重置索引值,否则在下方相等的时候会按照原有的索引值进行对等,顺序还是乱的
if len(NPY) == len(HS300_symbol_list):
factor_matrix["net_prof_yoy"] = NPY["net_prof_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
NPY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="net_prof_yoy",start_date=last_day_time, end_date=day_time)
NPY_value = NPY[-1]["net_prof_yoy"]
factor_matrix.loc[number, ["net_prof_yoy"]] = NPY_value
except:
factor_matrix.loc[number,["net_prof_yoy"]] = -999
# # 获取营业利润同比增长率数据
OPY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="oper_prof_yoy", date=day_time, df=True)
OPY = OPY.sort_values(["symbol"],ascending=False)
OPY = OPY.reset_index(drop=True)
if len(OPY) == len(HS300_symbol_list):
factor_matrix["oper_prof_yoy"] = OPY["oper_prof_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
OPY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="oper_prof_yoy",start_date=last_day_time, end_date=day_time)
OPY_value = OPY[-1]["oper_prof_yoy"]
factor_matrix.loc[number, ["oper_prof_yoy"]] = OPY_value
except:
factor_matrix.loc[number, ["oper_prof_yoy"]] = -999
# # 获取总资产同比增长率数据
TAY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="ttl_asset_yoy", date=day_time, df=True)
TAY = TAY.sort_values(["symbol"],ascending=False)
TAY = TAY.reset_index(drop=True)
if len(TAY) == len(HS300_symbol_list):
factor_matrix["ttl_asset_yoy"] = TAY["ttl_asset_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
TAY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="ttl_asset_yoy",start_date=last_day_time, end_date=day_time)
TAY_value = TAY[-1]["ttl_asset_yoy"]
factor_matrix.loc[number, ["ttl_asset_yoy"]] = TAY_value
except:
factor_matrix.loc[number, ["ttl_asset_yoy"]] = -999
# 获取经营活动产生的现金流量净额同比增长率数据
NCOY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="net_cf_oper_yoy", date=day_time, df=True)
NCOY = NCOY.sort_values(["symbol"],ascending=False)
NCOY = NCOY.reset_index(drop=True)
if len(NCOY) == len(HS300_symbol_list):
factor_matrix["net_cf_oper_yoy"] = NCOY["net_cf_oper_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
NCOY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="net_cf_oper_yoy",start_date=last_day_time, end_date=day_time)
NCOY_value = NCOY[-1]["net_cf_oper_yoy"]
factor_matrix.loc[number, ["net_cf_oper_yoy"]] = NCOY_value
except:
factor_matrix.loc[number, ["net_cf_oper_yoy"]] = -999
# 获取净资产同比增长率数据
NAY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="net_asset_yoy", date=day_time, df=True)
NAY = NAY.sort_values(["symbol"],ascending=False)
NAY = NAY.reset_index(drop=True)
if len(NAY) == len(HS300_symbol_list):
factor_matrix["net_asset_yoy"] = NAY["net_asset_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
NAY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="net_asset_yoy",start_date=last_day_time, end_date=day_time)
NAY_value = NAY[-1]["net_asset_yoy"]
factor_matrix.loc[number, ["net_asset_yoy"]] = NAY_value
except:
factor_matrix.loc[number, ["net_asset_yoy"]] = -999
# 获取基本每股收益同比增长率数据
EBY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="eps_bas_yoy", date=day_time, df=True)
EBY = EBY.sort_values(["symbol"],ascending=False)
EBY = EBY.reset_index(drop=True)
if len(EBY) == len(HS300_symbol_list):
factor_matrix["eps_bas_yoy"] = EBY["eps_bas_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
EBY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="eps_bas_yoy",start_date=last_day_time, end_date=day_time)
EBY_value = EBY[-1]["eps_bas_yoy"]
factor_matrix.loc[number, ["eps_bas_yoy"]] = EBY_value
except:
factor_matrix.loc[number, ["eps_bas_yoy"]] = -999
# 获取净资产收益率同比增长率(摊薄)数据
ROEY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="roe_yoy", date=day_time, df=True)
ROEY = ROEY.sort_values(["symbol"],ascending=False)
ROEY = ROEY.reset_index(drop=True)
if len(ROEY) == len(HS300_symbol_list):
factor_matrix["roe_yoy"] = ROEY["roe_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
ROEY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="roe_yoy",start_date=last_day_time, end_date=day_time)
ROEY_value = ROEY[-1]["roe_yoy"]
factor_matrix.loc[number, ["roe_yoy"]] = ROEY_value
except:
factor_matrix.loc[number, ["roe_yoy"]] = -999
# 获取利润总额同比增长率数据
TPY = gm.stk_get_finance_deriv_pt(symbols=HS300_symbol_list, fields="ttl_prof_yoy", date=day_time, df=True)
TPY = TPY.sort_values(["symbol"],ascending=False)
TPY = TPY.reset_index(drop=True)
if len(TPY) == len(HS300_symbol_list):
factor_matrix["ttl_prof_yoy"] = TPY["ttl_prof_yoy"]
else:
for number in range(len(HS300_symbol_list)):
try:
TPY = gm.stk_get_finance_deriv(symbol=HS300_symbol_list[number], fields="ttl_prof_yoy",start_date=last_day_time, end_date=day_time)
TPY_value = TPY[-1]["ttl_prof_yoy"]
factor_matrix.loc[number, ["ttl_prof_yoy"]] = TPY_value
except:
factor_matrix.loc[number, ["ttl_prof_yoy"]] = -999
"""
# 测试代码,与excel表中数据进行对比,看是否有误
test = gm.stk_get_finance_deriv(symbol="SZSE.301269", fields="eps_bas_yoy",start_date=last_day_time, end_date=day_time)
test_value = test[-1]["eps_bas_yoy"]
print(test_value)
"""
#从dataframe中提取数据矩阵
factor_matrix = factor_matrix.dropna() #删除包含NAN的非数据行,如果这一行中存在一个NAN,那么整行都会被删除
factor_matrix_useful = factor_matrix.iloc[:,1:] # 第0列数据是股票代码,不是因子没有用,因此从第1列开始
factor_matrix_useful = np.asmatrix(factor_matrix_useful) # 转换为矩阵
print(type(factor_matrix_useful))
#先进行列归一化,然后对每行进行标准化处理
factor_matrix_useful = min_max_scaling(factor_matrix_useful)
weight = [[1],[1],[1],[1],[1],[1],[1],[1]]
weight_mat = np.asmatrix(weight)
res = np.dot(factor_matrix_useful,weight_mat)
factor_matrix["score"] = res
factor_matrix = factor_matrix.sort_values(["score"],ascending=False)
factor_matrix = factor_matrix.reset_index(drop=True)
factor_matrix.to_csv("factor_matrix.csv")
symbol_list = []
for value in factor_matrix["symbol"].values:
symbol_list.append(value)
return symbol_list #返回股票列表
主函数我们另外新建一个py文件,在这里我命名为test3.py(随意写了),大家注意把策略名/文件名/token码都替换为自己的哈。
# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
import os
import numpy as np
import pandas as pd
import datetime
from dateutil.relativedelta import relativedelta
import grow_yinzi_strange
def init(context):
# 在init函数中设置全局变量
# algo执行定时任务函数,只能传context参数
# index股票池代码,沪深300
# num买卖股票数据,暂定5只
# schedule在指定时间自动执行策略算法, 通常用于选股类型策略,每月一次,早上9点31分执行定时任务
# date_rule执行频率,目前暂时支持1d、1w、1m,其中1w、1m仅用于回测,实时模式1d以上的频率,需要在algo判断日期
# time_rule执行时间, 注意多个定时任务设置同一个时间点,前面的定时任务会被后面的覆盖
context.index = "SHSE.000300"
context.num = 5
schedule(schedule_func=algo, date_rule='1m', time_rule='09:31:00') #设置回测方式,定时任务
def algo(context):
now = context.now #获取当前时间
order_close_all() # 这里为例简化,全部清仓上一阶段股票
target_list = grow_yinzi_strange.grow_yinzi(context.index,now)
target_symbol = target_list[:context.num] #取列表中前5只作为标的股票
print(target_symbol,now)
for symbol in target_symbol:
order_target_percent(symbol=symbol,percent=1/context.num,order_type=OrderType_Market,position_side=PositionSide_Long) #执行买入操作
# 查看最终的回测结果
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='test3.py', #自己的文件名
mode=MODE_BACKTEST,
token='自己的token码',
backtest_start_time='2023-08-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)
运行后结果如下:
可以看到这个结果一共运行了快6分钟,主要是数据库访问的时间比较慢。
从结果上看,这个策略还是负收益,超额收益率只有5.63%,不算是一个非常优秀的策略,所以我们可以调整因子或者调整权重进行测试,不断迭代,最终寻找收益率更高的策略。