【Python】CNN多因子选股模型(量化交易)

之前写的一些策略主要是基于时间序列下的个股交易方式(网格交易策略、动量策略等),这些策略不属于目前的主流策略,在现今只是起收益增厚的辅助作用。今天讲一个量化机构都在用的多因子选股模型,正巧见过一个券商研报用CNN来做,自己尝试做一下,熟悉一下架构。

模型涉及到多期回测,有点点复杂,可以看看下图,我尽量解释清楚各个模块。

 话不多说上代码

导入必要库:

import akshare as ak
import pandas as pd
import numpy as np
from pylab import plt, mpl
from datetime import datetime, timedelta
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense
from sklearn import preprocessing

查询投资范围的股票代码以及基本信息

# 查询股票代码
stock_list = ak.index_stock_cons_weight_csindex(symbol="000300")
noa = 300
# 查询行业 + 重构表格
data = []
for i in range(noa):
    所属行业 = ak.stock_individual_info_em(symbol=stock_list['成分券代码'][i])['value'][2]
    data.append([stock_list['成分券代码'][i], stock_list['权重'][i], 所属行业])
data = pd.DataFrame(data, columns= ['代码','权重','行业'])

第一个模块,上图所示黄色部分,定义函数计算单期因子以及对应涨跌幅。

def data_collection(T0):
    variables = []
    for i in range(noa):
        time_f=(datetime.strptime(T0,'%Y%m%d')+timedelta(days =10)).strftime('%Y%m%d') 
        time_30 = (datetime.strptime(T0,'%Y%m%d')-timedelta(days =30)).strftime('%Y%m%d')    
        s_code = stock_list['成分券代码'][i]
        data = ak.stock_zh_a_hist(symbol=s_code, period="daily", start_date=time_30, end_date=T0, adjust="qfq")
        MA_5 = data['收盘'][-6:-1].mean()
        MA_10 = data['收盘'][-11:-1].mean()
        换手率_10 = data['换手率'][-11:-1].mean()
        换手率_1 = data['换手率'][-2:-1].mean()
        涨跌幅_10 = data['涨跌幅'][-11:-1].mean()
        涨跌幅_1 = data['涨跌幅'][-2:-1].mean()
        振幅_10 = data['振幅'][-11:-1].mean()
        振幅_1 = data['振幅'][-2:-1].mean()
        涨跌幅_f = ak.stock_zh_a_hist(symbol=s_code, period="daily", 
                                   start_date=T0, 
                                   end_date=time_f,
                                   adjust="qfq")['涨跌幅'].mean()
        variables.append([MA_5, MA_10, 换手率_10, 换手率_1, 涨跌幅_10, 涨跌幅_1,振幅_10, 振幅_1, 涨跌幅_f])
    return variables

第二个模块,上图粉色部分,在当前阶段前移5次10天读取历史数据。

def train_data(T0):
    current_time = (datetime.strptime(T0,'%Y%m%d')-timedelta(days =10)).strftime('%Y%m%d')
    variable_set = pd.DataFrame()
    for i in range(5):
        variables = data_collection(current_time)
        variables = pd.DataFrame(variables)
        current_time = (datetime.strptime(current_time,'%Y%m%d')-timedelta(days =10)).strftime('%Y%m%d')
        variable_set =pd.concat([pd.DataFrame(variable_set), variables])
        variable_set = pd.DataFrame(preprocessing.MinMaxScaler().fit_transform(variable_set))
    return variable_set

第三块,上图绿色部分,适用数据训练出模型。

def model_training(variable_set):
# 准备数据
    X = variable_set.drop(8, axis=1).values
    y = variable_set[8].values

# 构建CNN模型
    model = Sequential()
    model.add(Conv1D(filters=32, kernel_size=3, activation='relu', input_shape=(X.shape[1], 1)))
    model.add(MaxPooling1D(pool_size=2))
    model.add(Flatten())
    model.add(Dense(64, activation='relu'))
    model.add(Dense(1, activation='linear'))

# 编译模型
    model.compile(loss='mean_squared_error', optimizer='adam')

# 模型训练
    model.fit(X, y, epochs=10, batch_size=32, validation_data=(X, y))

# 模型预测

    return model

第四个模块,上图蓝色部分,整体代码跑10次,得到10期的预测结果。

# 从23年开始,做10期
T0 = '20230101'
for i in range(10):
# 1.获取训练数据
    variable_set = train_data(T0)
# 2.训练
    model = model_training(variable_set)

# 3.获取回测数据
    T_f10 = (datetime.strptime(T0,'%Y%m%d')+timedelta(days=10)).strftime('%Y%m%d')
    test_set = pd.DataFrame(data_collection(T_f10))
# 4.获取预测
    X = test_set.drop(8, axis=1).values
    data[T0] = model.predict(X)
    data[T0] = data[T0].rank(ascending = True)
# 5.重置时间线
    T0 = T_f10

先读取一下期间行情价格,用于后续计算

s_close = pd.DataFrame()
s_close.index = ak.stock_zh_a_hist(symbol='000001', period="daily", start_date="20230101", end_date='20230401', adjust="qfq")['日期']

for i in range(noa):
    历史行情 = ak.stock_zh_a_hist(symbol=stock_list['成分券代码'][i], period="daily", start_date="20230101", end_date='20231230', adjust="qfq")[['日期','收盘']]
    历史行情.columns = ['date',str(stock_list['成分券代码'][i])]
    历史行情.index = 历史行情['date']
    s_close = s_close.join(历史行情[str(stock_list['成分券代码'][i])],how ='left')

# 计算日回报率
s_close.index = pd.to_datetime(s_close.index)
s_rets = np.log(s_close / s_close.shift(1))

计算部分,以及绘图部分看看结果

df = pd.DataFrame()
df = data.drop(['代码', '权重','行业'],axis=1)
df.columns = pd.to_datetime(df.columns)
df.index = data['代码']

# 期初资金1亿,不考虑手续费,不考虑行业偏离问题,就买分数大于100的,一个买500万的
portfolio = pd.DataFrame()
balance = []
init_c = 100000000
stock_num = 100

for i in range(len(df.columns)-1):
    buy_stock_price = s_close[(s_close.index>=df.columns[i])].iloc[0]
    buy_list = df[df.columns[i]].map(lambda x:1 if x>(300-stock_num) else None) 
    time_ = s_close[(s_close.index>=df.columns[i])].index[0].strftime('%Y%m%d')
    portfolio[time_]=(init_c/stock_num)//(buy_list* buy_stock_price)
    cash = init_c-(portfolio[time_]*buy_stock_price).sum()
    balance.append(cash)
    if i<(len(df.columns)-2):
        sell_stock_price = s_close[(s_close.index>=df.columns[i+1])].iloc[0]
        init_c = (portfolio[time_] * sell_stock_price).sum() + cash
    else:
        break
portfolio = portfolio.transpose().fillna(0)
portfolio['cash'] = balance
portfolio.index = pd.to_datetime(portfolio.index)


values = pd.DataFrame()
s_close['cash'] = 1
values.index = s_close.index
values = (values.join(portfolio,how = 'left').ffill() *s_close).sum(axis = 1)

plt.plot((s_rets*np.array(data['权重']/100)).sum(axis = 1).cumsum(),'r')
plt.plot(np.log(values / values.shift(1)).cumsum(),'g')
plt.show()

【Python】CNN多因子选股模型(量化交易)_第1张图片

因子是随便写的,算力和数据库有限,没有进行降维/重要性检验等过程,只做了归一化。

未考虑实际操作中的卖出手续费、卖出时间、账户金额限制问题,移仓全都默认发生在T0时间收盘竞价时,且全部成功买入/卖出。

你可能感兴趣的:(python,cnn,金融,神经网络)