多因子选股
```py
#encoding:gbk
import pandas as pd
import numpy as np
import talib
import time
from scipy import stats
import statsmodels.api as sm
from statsmodels import regression
import datetime
from datetime import timedelta
def init(ContextInfo):
g = ContextInfo #简化调用程序输入
g.g_tc=90 #调仓频率
g.g_yb=60 #样本长度
g.g_N=5 #持仓数目
g.g_t=0 #记录连续回测天数
g.g_rf=0.04 #无风险利率
g.accID = 'test' #账号
g.Buy_price = {} #记录买入价格,用于止盈止损
g.stop_profit = 0.4 #止盈比率
g.stop_loss = 0.1 #止损比率
def handlebar(ContextInfo):
g=ContextInfo
d = g.barpos #当前K线索引号
time= timetag_to_datetime(g.get_bar_timetag(d),'%Y%m%d %H:%M:%S') #当前K线时间
end= datetime.datetime.strptime(time,'%Y%m%d %H:%M:%S') #转换成日期格式
begin= end-timedelta(days=g.g_yb) #回归开始时间
startDate = datetime.datetime.strftime(begin,'%Y%m%d %H:%M:%S')[0:8] #转换成字符串格式
endDate = datetime.datetime.strftime(end,'%Y%m%d %H:%M:%S')[0:8]
p = g.get_net_value(d) #策略净值
realtime= g.get_bar_timetag(d)
stocks= g.get_sector('000300.SH',realtime) #沪深300成分股
LoS=len(stocks) # 300
#符合调仓频率进行调仓计算-------------------------------------------------------------------------
if g.g_t%g.g_tc==0:# and up_trend: #调仓频率
portfolio_positions= get_holding(g.accID,'STOCK') #获取持仓股票
LoS=len(stocks) # 300
MC_fieldList = ['CAPITALSTRUCTURE.total_capital'] #总股数字段
equities_fieldList = ['ASHAREBALANCESHEET.tot_liab_shrhldr_eqy'] #股票权益字段
#查询并计算账面市值比因子---------------------------------------------------------------------------------
total_capital= g.get_financial_data(MC_fieldList, stocks, startDate, startDate) #获取总股数
close =g.get_market_data(['close'],stocks,start_time=startDate,end_time=startDate,#获取收盘价
skip_paused=True,period='1d',dividend_type=g.dividend_type,count= -1)
equities = g.get_financial_data(equities_fieldList, stocks, startDate, startDate) #获取权益,输出dataframe
df=total_capital*0
df.columns = ['market_cap'] #市值
for i in stocks:
if close[i]['close'].any()>0: #过滤数据未获取到导致的程序错误
df.loc[i][0] = total_capital.loc[i][0]*close[i]['close'][0] #总股数*市价,close是panel,close[i]返回股票i的收盘价Series
market_cap = pd.Series(df['market_cap'],index = df.index) #账面价值
equities = g.get_financial_data(equities_fieldList, stocks, startDate, startDate) #获取权益,输出dataframe
equities.columns = ['total_owner_equities'] #总权益
total_owner_equities = pd.Series(equities['total_owner_equities'],index=equities.index)#将权益格式变为Series
BTM = pd.Series(index=stocks) #创建账面市值比变量
for k in stocks:
BTM[k] = total_owner_equities[k]/market_cap[k] #计算账面市值比
#选出特征股票组合----------------------------------------------------------------------------------------
BS_code_list = market_cap.sort_values().index #按市值升序排列的股票列表
S=BS_code_list[:int(LoS/3)] #s是小市值股票列表
B=BS_code_list[int(LoS-LoS/3):] #B是大市值股票列表
LH_code_list = BTM.sort_values().index #按账面市值比升序排列的股票列表
L=LH_code_list[:int(LoS/3)] #L是小账面市值比股票列表
H=LH_code_list[int(LoS-LoS/3):] #H是大账面市值比股票列表
#获得样本期间的股票价格并计算日收益率
df2 = g.get_market_data(['close'],stocks,start_time=startDate,
end_time=endDate,skip_paused=True,period='1d',dividend_type=g.dividend_type,count= -1)
close_stocks = pd.DataFrame(columns=stocks) #设置格式
for i in stocks:
close_stocks[i]= df2[i]['close'] #df2是panel数据,将其转化为DataFrame
close_stocks = close_stocks.fillna(1) #fillna(1)用1填充缺失值
df4 = np.diff(np.log(close_stocks),axis=0)+0*close_stocks[1:]
#求因子的值
SMB= (df4[S].sum(axis=1)/len(S)-df4[B].sum(axis=1)/len(B))
HML= (df4[H].sum(axis=1)/len(H)-df4[L].sum(axis=1)/len(L))
rf=g.g_rf/252 #无风险利率
dp= g.get_market_data(['close'],['000300.SH'],start_time=startDate,end_time=endDate,#用沪深300作为大盘基准
skip_paused=True,period='1d',dividend_type=g.dividend_type,count= -1)
RM=np.diff(np.log(dp),axis=0)+0*dp[1:]-rf #计算大盘指数超额收益
RM=pd.Series(RM['close'])
X=pd.DataFrame({"RM":RM,"SMB":SMB,"HML":HML}) #保存计算好的因子
factor_flag=["RM","SMB","HML"]
#对样本数据进行线性回归并计算ai
t_scores=[0.0]*LoS
#计算每只股票的alpha
for i in range(LoS):
t_stock=stocks[i]
sample=pd.DataFrame()
t_r=linreg(X,df4[t_stock]-g.g_rf/252,len(factor_flag)) #调用线性回归函数
t_scores[i]=t_r[0] #保存第一个系数
scores=pd.DataFrame({'code':stocks,'score':t_scores}) #这scores就是alpha
stock_sort=scores.sort_values('score')['code'] #依打分排序,当前需要持仓的股票
buy_stocks=stock_sort[:g.g_N] #选择前N只股票作为新的股票池
#对于不需要持仓的股票,全仓卖出----------------------------------------------------------------------
portfolio_positions= get_holding(g.accID,'STOCK') #查询持仓
if portfolio_positions !=0:
for k in portfolio_positions:
if k not in buy_stocks: #如果不在股票池的股票,清仓卖出
tock_sell = k #保存需要卖出股票代码
order_target_value(tock_sell,0,g,g.accID) #卖出该股票
g.Buy_price[k]=0 #买入价清零
#买进新的股票池股票--------------------------------------------------------------------------------
g_everyStock=get_totalvalue(g.accID,'STOCK')/g.g_N #为每个持仓股票平均分配资金
for k in buy_stocks: #买入股票池股票
order_target_value(k,g_everyStock,g,g.accID) #
Buy_price= g.get_market_data(['close'],[k],#start_time=endDate,end_time=endDate,
skip_paused=True,period='1d',dividend_type=g.dividend_type,count= -1)
g.Buy_price[k] = Buy_price #保存买入价
#============止盈止损============================================================================
g.g_t+=1 #调仓频率(天数)
portfolio_positions= get_holding(g.accID,'STOCK') #查询持仓
if len(portfolio_positions)>0:
for i in portfolio_positions: #按持仓循环
current_price= g.get_market_data(['close'],[i],
skip_paused=True,period='1d',dividend_type=g.dividend_type,count= -1)
#价格下降n%止损----------------------------------------
if g.Buy_price[i]>0 and current_price<(1-g.stop_loss)*g.Buy_price[i]:
order_target_value(i,0,ContextInfo,g.accID) #卖出该股票仓位
g.Buy_price[i]=0 #清仓后,将买入价归零
g.draw_text(1>0,p+0.01*p,"S") #画出止损点
#价格上升n%止盈----------------------------------------
if g.Buy_price[i]>0 and current_price>(1+g.stop_profit)*g.Buy_price[i]:
order_target_value(i,0,ContextInfo,g.accID)
g.Buy_price[i]=0
g.draw_text(1>0,p+0.01*p,"Y") #画出止盈点
#获取账户总权益m_dBalance
def get_totalvalue(accountid,datatype):
result=0
resultlist=get_trade_detail_data(accountid,datatype,"ACCOUNT")
for obj in resultlist:
result=obj.m_dAvailable #可用资金
return result
#获取持仓股票列表m_dBalance
def get_holding(accountid,datatype):
holdings=[]
resultlist=get_trade_detail_data(accountid,datatype,"POSITION")
for obj in resultlist:
result=obj.m_strInstrumentID + '.' + obj.m_strExchangeID
holdings.append(result)
return holdings
# 辅助线性回归的函数
def linreg(X,Y,columns=3):
X=sm.add_constant(np.array(X))
Y=np.array(Y)
if len(Y)>1:
results = regression.linear_model.OLS(Y,X).fit() #线性回归最小二乘法
return results.params
else:
return [float("nan")]*(columns+1)
```