风险模型源码如下
# 导入函数库
from jqdata import *
import datetime
import numpy as np
import pandas as pd
import time
from jqdata import *
from pandas import Series, DataFrame
from sklearn.linear_model import LinearRegression
from jqfactor import get_factor_values
# 初始化函数,设定基准等等
def initialize(context):
set_params() #1 设置策参数
set_variables() #2 设置中间变量
set_backtest() #3 设置回测条件
# 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入'000300.XSHG'或'510300.XSHG'是一样的)
# 开盘前运行,按月运行
run_monthly(before_trading_start, monthday = 1, time = 'open', reference_security='000906.XSHG')
# 开盘时运行
run_monthly(trade, monthday = 1, time = 'open', reference_security='000906.XSHG')
# 收盘后运行
run_daily(after_trading_end, time='after_close', reference_security='000906.XSHG')
#1 设置策略参数
def set_params():
#'BP','inc_net_profit_year_on_year','operating_profit','inc_revenue_year_on_year','roe','ps_ratio','pe_ratio','mom_month','mom_quarter','VOL60'
g.factor = ['ps_ratio','pe_ratio','pb_ratio','circulating_cap','circulating_market_cap','turnover_ratio','market_cap','pcf_ratio'] # 当前回测的因子
g.shift = 126 # 设置一个观测天数(天数)
g.percent = 0.1 # 持仓占可选股票池比例
g.index = '000906.XSHG' # 定义股票池,中证800
g.sort_rank = True # 设定选取sort_rank: True 为最大,False 为最小
g.rf = 0.04 # 设定无风险收益率为4%
#2 设置中间变量
def set_variables():
g.feasible_stocks = [] # 当前可交易股票池
g.num_stocks = 0 # 设置持仓股票数目
#3 设置回测条件
def set_backtest():
set_benchmark('000906.XSHG') # 设置中证800为基准
set_option('use_real_price', True) # 用真实价格交易
log.info('初始函数开始运行且全局只运行一次') # 输出内容到日志 log.info()
log.set_level('order', 'error') # 设置报错等级,过滤掉order系列API产生的比error级别低的log
'''
================================================================================
每天开盘前
================================================================================
'''
#每天开盘前要做的事情
def before_trading_start(context):
# 输出运行时间
log.info('函数运行时间(before_market_open):'+str(context.current_dt.time()))
# 设置可行股票池:获得当前开盘的股票池并剔除当前或者计算样本期间停牌的股票
g.feasible_stocks = set_feasible_stocks(get_index_stocks(g.index),g.shift,context)
# 设置滑点与手续费
set_slip_fee(context)
# 设置可行股票池
# 过滤掉当日停牌的股票,且筛选出前days天未停牌股票
# 输入:stock_list为list类型,样本天数days为int类型,context(见API)
# 输出:list=g.feasible_stocks
def set_feasible_stocks(stock_list,days,context):
# 得到是否停牌信息的dataframe,停牌的1,未停牌得0
suspened_info_df = get_price(list(stock_list),
start_date=context.current_dt,
end_date=context.current_dt,
frequency='daily',
fields='paused'
)['paused'].T
# 过滤停牌股票 返回dataframe
unsuspened_index = suspened_info_df.iloc[:,0]<1
# 得到当日未停牌股票的代码list:
unsuspened_stocks = list(suspened_info_df[unsuspened_index].index)
# 进一步,筛选出前days天未曾停牌的股票list
'''
feasible_stocks = []
current_data = get_current_data()
for stock in unsuspened_stocks:
if sum(attribute_history(stock,
days,
unit = '1d',
fields = ('paused'),
skip_paused = False
)
)[0] == 0:
feasible_stocks.append(stock)
'''
#剔除ST股
st_data = get_extras('is_st', unsuspened_stocks, end_date = context.previous_date, count = 1)
stockList = [stock for stock in unsuspened_stocks if not st_data[stock][0]]
return stockList
# 根据不同的时间段设置滑点与手续费(永远不变的函数)
def set_slip_fee(context):
# 将滑点设置为0
set_slippage(FixedSlippage(0))
# 根据不同的时间段设置手续费
dt=context.current_dt
if dt>datetime.datetime(2013,1, 1):
set_commission(PerTrade(buy_cost=0.0003,
sell_cost=0.0013,
min_cost=5))
elif dt>datetime.datetime(2011,1, 1):
set_commission(PerTrade(buy_cost=0.001,
sell_cost=0.002,
min_cost=5))
elif dt>datetime.datetime(2009,1, 1):
set_commission(PerTrade(buy_cost=0.002,
sell_cost=0.003,
min_cost=5))
else:
set_commission(PerTrade(buy_cost=0.003,
sell_cost=0.004,
min_cost=5))
'''
================================================================================
每天交易时
================================================================================
'''
## 开盘时运行函数
def trade(context):
# 输出运行时间
log.info('函数运行时间(trade):'+str(context.current_dt.time()))
# 按alpha由小到大排序
holding_list = get_stocks(g.feasible_stocks, context, asc = g.sort_rank)
# 计算holding_list长度
total_number = len(holding_list)
# 购买股票为total_number的对应比例股票
g.num_stocks = int(total_number * g.percent)
# 提取需要的分位信息
(start_q, end_q) = (0,100*g.percent)
# 重新调整仓位,输入context,使用信号结果holding_list
rebalance(context, holding_list, start_q, end_q, total_number)
#7 建立多元线性回归模型,选择最小的alpha
def get_stocks(stocks_list,context,asc):
# 获取所有股票的收益率数据
price = get_price(stocks_list, end_date = context.previous_date, count = 1+g.shift, fields = ['close'])['close']
ret = price/price.shift(1) - 1
# 获取因子数据
df_ps_ratio = get_df_ps_ratio(context,stocks_list)
df_pe_ratio = get_df_pe_ratio(context,stocks_list)
df_circulating_cap = get_df_circulating_cap(context,stocks_list)
df_circulating_market_cap = get_df_circulating_market_cap(context,stocks_list)
df_turnover_ratio = get_df_turnover_ratio(context,stocks_list)
df_pb_ratio = get_df_pb_ratio(context,stocks_list)
df_market_cap = get_df_market_cap(context,stocks_list)
df_pcf_ratio = get_df_pcf_ratio(context,stocks_list)
# 定义inter_list与code_list存储变量
inter_list = []
code_list = []
# 对每一支股票的收益率数据与拿到的因子值进行多元回归
for i in stocks_list:
# 减去无风险收益率
ret_rf = ret.loc[:,i] - g.rf
ret_rf.name = 'ret'
#将ret_rf与df_ps_ratio合并
result = pd.concat([df_ps_ratio,df_pb_ratio,df_pe_ratio,df_pcf_ratio,df_market_cap,df_circulating_cap,df_circulating_market_cap,df_turnover_ratio,ret_rf],axis = 1)
result = result.dropna()
if len(result) == 0:
continue
else:
#定义回归的x、y
x = result[g.factor]
y = result['ret']
#建模
lrModel = LinearRegression()
#训练模型
lrModel.fit(x,y)
#获取截距项
inter = lrModel.intercept_
#将截距项存入一个list中
inter_list.append(inter)
#将股票代码存入另一个list
code_list.append(i)
# 对alpha从小到大进行排序
df_inter = pd.DataFrame({'inters':inter_list,'code':code_list})
df_inter.index = df_inter['code']
stocks = list(df_inter.sort('inters',ascending = asc).index)
# 返回排完序的股票代码
return stocks
# 本部分为获取所需的因子数据,一个因子用一个函数控制
#定义获取ps_ratio的函数,传入起止日期,返回FF规则下计算的因子
def get_df_ps_ratio(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.ps_ratio')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['ps_ratio'] <= data_ret['ps_ratio'].quantile(1/3)]
big = data_ret[data_ret['ps_ratio'] >= data_ret['ps_ratio'].quantile(2/3)]
#按市值加权,计算权重(仿照Fama-French三因子算法)
small['weight'] = small['market_cap']/small['market_cap'].sum()
big['weight'] = big['market_cap']/big['market_cap'].sum()
#small组减去big组
temp = (small['ret']*small['weight']).sum() - (big['ret']*big['weight']).sum()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'ps_ratio':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
#定义获取pe_ratio的函数,传入起止日期,返回FF规则下计算的因子
def get_df_pe_ratio(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.pe_ratio')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['pe_ratio'] <= data_ret['pe_ratio'].quantile(1/3)]
big = data_ret[data_ret['pe_ratio'] >= data_ret['pe_ratio'].quantile(2/3)]
#按市值加权,计算权重(仿照Fama-French三因子算法)
small['weight'] = small['market_cap']/small['market_cap'].sum()
big['weight'] = big['market_cap']/big['market_cap'].sum()
#small组减去big组
temp = (small['ret']*small['weight']).sum() - (big['ret']*big['weight']).sum()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'pe_ratio':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
#定义获取circulating_cap的函数,传入起止日期,返回FF规则下计算的因子
def get_df_circulating_cap(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.circulating_cap')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['circulating_cap'] <= data_ret['circulating_cap'].quantile(1/3)]
big = data_ret[data_ret['circulating_cap'] >= data_ret['circulating_cap'].quantile(2/3)]
#按市值加权,计算权重(仿照Fama-French三因子算法)
small['weight'] = small['market_cap']/small['market_cap'].sum()
big['weight'] = big['market_cap']/big['market_cap'].sum()
#small组减去big组
temp = (small['ret']*small['weight']).sum() - (big['ret']*big['weight']).sum()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'circulating_cap':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
#定义获取circulating_market_cap的函数,传入起止日期,返回FF规则下计算的因子
def get_df_circulating_market_cap(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.circulating_market_cap')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['circulating_market_cap'] <= data_ret['circulating_market_cap'].quantile(1/3)]
big = data_ret[data_ret['circulating_market_cap'] >= data_ret['circulating_market_cap'].quantile(2/3)]
#按市值加权,计算权重(仿照Fama-French三因子算法)
small['weight'] = small['market_cap']/small['market_cap'].sum()
big['weight'] = big['market_cap']/big['market_cap'].sum()
#small组减去big组
temp = (small['ret']*small['weight']).sum() - (big['ret']*big['weight']).sum()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'circulating_market_cap':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
#定义获取turnover_ratio的函数,传入起止日期,返回FF规则下计算的因子
def get_df_turnover_ratio(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.turnover_ratio')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['turnover_ratio'] <= data_ret['turnover_ratio'].quantile(1/3)]
big = data_ret[data_ret['turnover_ratio'] >= data_ret['turnover_ratio'].quantile(2/3)]
#按市值加权,计算权重(仿照Fama-French三因子算法)
small['weight'] = small['market_cap']/small['market_cap'].sum()
big['weight'] = big['market_cap']/big['market_cap'].sum()
#small组减去big组
temp = (small['ret']*small['weight']).sum() - (big['ret']*big['weight']).sum()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'turnover_ratio':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
#定义获取pb_ratio的函数,传入起止日期,返回FF规则下计算的因子
def get_df_pb_ratio(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.pb_ratio')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['pb_ratio'] <= data_ret['pb_ratio'].quantile(1/3)]
big = data_ret[data_ret['pb_ratio'] >= data_ret['pb_ratio'].quantile(2/3)]
#按市值加权,计算权重(仿照Fama-French三因子算法)
small['weight'] = small['market_cap']/small['market_cap'].sum()
big['weight'] = big['market_cap']/big['market_cap'].sum()
#small组减去big组
temp = (small['ret']*small['weight']).sum() - (big['ret']*big['weight']).sum()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'pb_ratio':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
#获取market_cap因子,传入起止日期,返回FF规则下计算的因子
def get_df_market_cap(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.market_cap')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['market_cap'] <= data_ret['market_cap'].quantile(1/3)]
big = data_ret[data_ret['market_cap'] >= data_ret['market_cap'].quantile(2/3)]
#small组减去big组
temp = small['ret'].mean() - big['ret'].mean()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'market_cap':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
#定义获取pcf_ratio的函数,传入起止日期,返回FF规则下计算的因子
def get_df_pcf_ratio(context,stocks_list):
#获取日期列表
stock_data = get_price(g.index,end_date = context.previous_date,count = g.shift,fields=['close'])
date_list = stock_data.index.tolist()
#建立一个空的ff_factor列表和day_list列表存储数据
ff_factor = []
day_list = []
#获取每一天的因子数据
for day in date_list:
#获取因子数据
factor_eval = eval('valuation.pcf_ratio')
q = query(valuation.code,factor_eval,valuation.market_cap).filter(valuation.code.in_(stocks_list))
factor_data = get_fundamentals(q,date = day)
factor_data.index = factor_data['code']
del factor_data['code']
#获取收盘价数据,然后计算得到收益率数据
price = get_price(stocks_list,end_date = day,count = 2)
price = price['close'].T
ret = pd.DataFrame(price[price.columns[1]]/price[price.columns[0]] - 1)
ret.columns = ['ret']
#收盘价数据与因子数据合并
data_ret = pd.concat([factor_data,ret],axis = 1)
data_ret = data_ret.dropna()
if len(data_ret) > 2:
#分别获取小factor和大factor组数据
small = data_ret[data_ret['pcf_ratio'] <= data_ret['pcf_ratio'].quantile(1/3)]
big = data_ret[data_ret['pcf_ratio'] >= data_ret['pcf_ratio'].quantile(2/3)]
#按市值加权,计算权重(仿照Fama-French三因子算法)
small['weight'] = small['market_cap']/small['market_cap'].sum()
big['weight'] = big['market_cap']/big['market_cap'].sum()
#small组减去big组
temp = (small['ret']*small['weight']).sum() - (big['ret']*big['weight']).sum()
ff_factor.append(temp)
day_list.append(day)
print day
else:
continue
#将结果整理为df
df = pd.DataFrame({'date':day_list,'pcf_ratio':ff_factor})
df.index = df['date']
#删除无用列
del df['date']
return df
# 依本策略的买入信号,得到应该买的股票列表
# 借用买入信号结果,不需额外输入
# 输入:context(见API)
def rebalance(context, holding_list, start_q, end_q, total_number):
if end_q == 100:
end_q = 100
# 每只股票购买金额
every_stock = context.portfolio.portfolio_value/g.num_stocks
# 空仓只有买入操作
if len(list(context.portfolio.positions.keys())) == 0:
for stock_to_buy in holding_list[int(start_q * total_number / 100) : int(end_q * total_number / 100)]:
order_target_value(stock_to_buy, every_stock)
else :
# 不是空仓先卖出持有但是不在购买名单中的股票
for stock_to_sell in list(context.portfolio.positions.keys()):
if stock_to_sell not in holding_list[int(start_q * total_number / 100) : int(end_q * total_number / 100)]:
order_target_value(stock_to_sell, 0)
# 因order函数调整为顺序调整,为防止先行调仓股票由于后行调仓股票占金额过大不能一次调整到位,这里运行两次以解决这个问题
for stock_to_buy in holding_list[int(start_q * total_number / 100) : int(end_q * total_number / 100)]:
order_target_value(stock_to_buy, every_stock)
for stock_to_buy in holding_list[int(start_q * total_number / 100) : int(end_q * total_number / 100)]:
order_target_value(stock_to_buy, every_stock)
'''
================================================================================
每天收盘后
================================================================================
'''
## 收盘后运行函数
def after_trading_end(context):
log.info(str('函数运行时间(after_market_close):'+str(context.current_dt.time())))
log.info('一天结束')
log.info('##############################################################')
2019-03-06