前言
现在市场上很多基金,特别是量化的基金,多带有“多因子”配置,策略等,这些基金策略实际上与之前《13.实盘交易策略制定与实施》是一致的,在量化交易,因子作为交易的基础(数据源),起着重要作用。从本文开始,从最简单的一个因子开始,讲解产生、使用、作用和检验,本文就这些因子是如何产生的,来做一个详细说明。
获取数据
为了获取数据方便,直接使用量化平台的数据,国内开放的量化平台有聚宽、果仁、米筐、优矿等,我这里有使用的是“聚宽”平台。
小伙伴们有这样的一个认知:“某日该股票价格上涨,且主力资金净流入的话,次日股价,可能上涨;否则下跌”,因此暂以资金流入/流出数据做为因子,进行实验验证,现以招商银行(600036)为例。
#导入jqdata的全部函数
from jqdata import *
#使用get_money_flow函数获取
df = get_money_flow('600036.XSHG',
fields = ['date',
#股票代码
'sec_code',
#涨跌幅
'change_pct',
#主力金额,包括超大单和大单
'net_amount_main',
#主力成交额占总成交额的比例
'net_pct_main'],
#设置好起止日期
start_date = '2023-03-09',
end_date = '2025-03-09')
#查看数据
df.head()
- | date | sec_code | change_pct | net_amount_main | net_pct_main |
---|---|---|---|---|---|
0 | 2023-03-09 | 600036.XSHG | -1.17 | -18056.3680 | -9.3621 |
1 | 2023-03-10 | 600036.XSHG | -1.55 | -30406.6513 | -15.4844 |
2 | 2023-03-13 | 600036.XSHG | 0.06 | -6289.9216 | -3.0741 |
3 | 2023-03-14 | 600036.XSHG | -1.34 | -28301.3136 | -13.0485 |
4 | 2023-03-15 | 600036.XSHG | 0.87 | -21240.4128 | -8.9221 |
特征工程
给这个的数据增加两个新的字段,其中一个是up_or_down,用来表示当日股价是上涨还是下跌。如果change_pct(涨幅)这个字段为正数,说明股价上涨,则up_or_down用1来表示,反之,用0表示,代表当日股价下跌。
类似地,我们用money_in_out字段表示主力资金净流入还是净流出。如果net_amount_main大于0,说明主力资金净流入,则在money_in_out字段用1表示:反之说明主力资金净流出,money_in_out字段用0表示。示例代码如下:
#增加一个字段,记录股价上涨还是下跌
#如果股价上涨,则以1标记,否则以0标记
df['up_or_down'] = np.where(df['change_pct']>0,1,0)
#在增加一个字段,记录主力资金净流入还是流出
#如果净流入,标记为1,否则标记为0
df['money_in_out'] = np.where(df['net_amount_main']>0,1,0)
#检查是否成功
df.head()
- | date | sec_code | change_pct | net_amount_main | net_pct_main | up_or_down | money_in_out |
---|---|---|---|---|---|---|---|
0 | 2023-03-09 | 600036.XSHG | -1.17 | -18056.3680 | -9.3621 | 0 | 0 |
1 | 2023-03-10 | 600036.XSHG | -1.55 | -30406.6513 | -15.4844 | 0 | 0 |
2 | 2023-03-13 | 600036.XSHG | 0.06 | -6289.9216 | -3.0741 | 1 | 0 |
3 | 2023-03-14 | 600036.XSHG | -1.34 | -28301.3136 | -13.0485 | 0 | 0 |
4 | 2023-03-15 | 600036.XSHG | 0.87 | -21240.4128 | -8.9221 | 1 | 0 |
计算
现在我们有了两个新的特征,能够体现股价的涨跌和主力资金的流入/流出情况,下面就可以用这两个新的特征来计算“瓦氏因子”了,咱们先说说思路,如果我们把两个特征相乘,则当股价上涨,且主力资金净流入时,因子值就是up_or_down 乘以 money_in_ out,也就是1X1,结果是1;而其他情况,“瓦氏因子”的数值都为0。例如,股价下跌但主力资金净流入,“瓦氏因子”为0x1,结果为0。同时,为了后面便于模型训练,我们还要做一个标签(即次日股票上涨还是下跌),存储在next_day 字段中。代码如下:
#瓦氏因子来了,用两个自增的字段相乘,得出因子值
df['factor_wa'] = df['up_or_down'] * df['money_in_out']
#再把次日涨跌作为预测标签存储到‘next_day’字段
df['next_day'] = df['up_or_down'].shift(-1)
#检查是否成功
df.head()
- | date | sec_code | change_pct | net_amount_main | net_pct_main | up_or_down | money_in_out | factor_wa | next_day |
---|---|---|---|---|---|---|---|---|---|
0 | 2023-03-09 | 600036.XSHG | -1.17 | -18056.3680 | -9.3621 | 0 | 0 | 0 | 0.0 |
1 | 2023-03-10 | 600036.XSHG | -1.55 | -30406.6513 | -15.4844 | 0 | 0 | 0 | 1.0 |
2 | 2023-03-13 | 600036.XSHG | 0.06 | -6289.9216 | -3.0741 | 1 | 0 | 0 | 0.0 |
3 | 2023-03-14 | 600036.XSHG | -1.34 | -28301.3136 | -13.0485 | 0 | 0 | 0 | 1.0 |
4 | 2023-03-15 | 600036.XSHG | 0.87 | -21240.4128 | -8.9221 | 1 | 0 | 0 | 0.0 |
准备训练
#还是请出已经熟悉的KNN算法
from sklearn.neighbors import KNeighborsClassifier
#导入数据集拆分工具
from sklearn.model_selection import train_test_split
#当然还需要有pandas
import pandas as pd
#数据集中把日期、股票代码,以及我们添加的特征去掉
dataset = df.drop(['date',
'sec_code',
'up_or_down',
'money_in_out'],
axis=1)
#检查是否成功
dataset.head()
- | change_pct | net_amount_main | net_pct_main | factor_wa | next_day |
---|---|---|---|---|---|
0 | -1.17 | -18056.3680 | -9.3621 | 0 | 0.0 |
1 | -1.55 | -30406.6513 | -15.4844 | 0 | 1.0 |
2 | 0.06 | -6289.9216 | -3.0741 | 0 | 0.0 |
3 | -1.34 | -28301.3136 | -13.0485 | 0 | 1.0 |
4 | 0.87 | -21240.4128 | -8.9221 | 0 | 0.0 |
#将‘next_day’以外的字段,作为数据集的特征
X = dataset.drop(['next_day'],axis=1)[:-1]
#将‘next_day’作为数据集的标签
y = dataset['next_day'][:-1]
#将数据集拆分为训练集与验证集
X_train, X_test, y_train, y_test =\
train_test_split(X, y, random_state = 574)
#创建KNN分类器,n_neighbors参数依然取95
knn = KNeighborsClassifier(n_neighbors=95)
#使用训练集训练模型
knn.fit(X_train, y_train)
#打印训练集中模型准确率
print(knn.score(X_train, y_train))
#打印验证集中模型准确率
print(knn.score(X_test,y_test))
0.5429362880886427
0.49586776859504134
小结
这里的结果并不是很理想,准确率只有0.4958% 与盲猜并不多,不过没关系,咱简单的用了几个因子,进行了建模,而实际因子有上百个,甚至上千个,这些因子怎么来帮助我们的选择股票呢,这个需要选择标的问题,而不是上面的仅仅对一支股票进行分析,而对一类股票进行分析,选择分数高的,或排名前的股票进行投资。
在聚宽平台上有专用的因子库,来获取相应的因子值。
选择标的
思路是选超大盘股,净利润率高,且高速成长的
get_all_securities
查询到全部指数
#指定get_all_securities的types参数为index
#即可查询全部指数
indices = get_all_securities(types=['index'])
#查看前二十条结果
indices.head(20)
- | display_name | name | start_date | end_date | type |
---|---|---|---|---|---|
000001.XSHG | 上证指数 | SZZS | 1991-07-15 | 2200-01-01 | index |
000002.XSHG | A股指数 | AGZS | 1992-02-21 | 2200-01-01 | index |
000003.XSHG | B股指数 | BGZS | 1992-02-21 | 2200-01-01 | index |
000004.XSHG | 工业指数 | GYZS | 1993-05-03 | 2200-01-01 | index |
000005.XSHG | 商业指数 | SYZS | 1993-05-03 | 2200-01-01 | index |
000006.XSHG | 地产指数 | DCZS | 1993-05-03 | 2200-01-01 | index |
000007.XSHG | 公用指数 | GYZS | 1993-05-03 | 2200-01-01 | index |
000008.XSHG | 综合指数 | ZHZS | 1993-05-03 | 2200-01-01 | index |
000009.XSHG | 上证380 | SZ380 | 2010-11-29 | 2200-01-01 | index |
000010.XSHG | 上证180 | SZ180 | 2002-07-01 | 2200-01-01 | index |
000011.XSHG | 基金指数 | JJZS | 2000-06-09 | 2200-01-01 | index |
000012.XSHG | 国债指数 | GZZS | 2003-01-02 | 2200-01-01 | index |
000013.XSHG | 上证企业债指数 | QZZS | 2003-06-09 | 2200-01-01 | index |
000015.XSHG | 红利指数 | HLZS | 2005-01-04 | 2200-01-01 | index |
000016.XSHG | 上证50 | SZ50 | 2004-01-02 | 2200-01-01 | index |
000017.XSHG | 新综指 | XZZ | 2006-01-04 | 2200-01-01 | index |
000018.XSHG | 180金融 | 180JR | 2007-12-10 | 2200-01-01 | index |
000019.XSHG | 治理指数 | ZLZS | 2008-01-02 | 2200-01-01 | index |
000020.XSHG | 中型综指 | ZXZZ | 2008-05-12 | 2200-01-01 | index |
000021.XSHG | 180治理 | 180ZL | 2008-09-10 | 2200-01-01 | index |
- 获取市值因子
#这里需要导入聚宽因子库的get_factor_values函数
from jqfactor import get_factor_values
#导入聚宽的因子分析库
from jqfactor import analyze_factor
#使用get_factor_values函数获取沪深300成分股的市值
factor_mc=get_factor_values(securities=get_index_stocks('000300.XSHG'), factors=['market_cap'],
end_date='2025-03-09',count=1)['market_cap']
#检查结果
factor_mc.T.head()
code | 2025-03-07 00:00:00 |
---|---|
000001.XSHE | 2.264671e+11 |
000002.XSHE | 8.983824e+10 |
000063.XSHE | 1.783780e+11 |
000100.XSHE | 8.957622e+10 |
000157.XSHE | 7.185378e+10 |
- 获取现金流因子
- 获取净利率因子
- 获取净利润增长因子
#get_factor_values中
#factors参数传入cash_flow_to_price_ratio
#即可获得市现率的倒数
factor_cfp = get_factor_values(securities = get_index_stocks('000300.XSHG'),
factors = ['cash_flow_to_price_ratio'],
end_date = '2025-03-09',
count = 1)['cash_flow_to_price_ratio']
#使用net_profit_ratio作为factors参数
#即可查询到企业的净利润率
factor_npr = get_factor_values(securities = get_index_stocks('000300.XSHG'),
factors = ['net_profit_ratio'],
end_date = '2025-03-09',
count = 1)['net_profit_ratio']
#使用net_profit_growth_rate作为factors参数
#即可查询到企业的净利润增长率
factor_npgr = get_factor_values(securities = get_index_stocks('000300.XSHG'),
factors = ['net_profit_growth_rate'],
end_date = '2025-03-09',
count = 1)['net_profit_growth_rate']
因子打包
将获取的因子打包成一个dataframe
#新建一个DataFrame,和前面市值数据保持同样的序号
factors = pd.DataFrame(index = factor_mc.T.index)
#在新的DataFrame中创建4个字段
#分别把市值、市现率倒数、净利润率、净利润增长率存储到其中
factors['mc'] = factor_mc.T['2025-03-07 00:00:00']
factors['cfp'] = factor_cfp.T['2025-03-07 00:00:00']
factors['npr'] = factor_npr.T['2025-03-07 00:00:00']
factors['npgr'] = factor_npgr.T['2025-03-07 00:00:00']
#检查结果
factors.head()
code | mc | cfp | npr | npgr |
---|---|---|---|---|
000001.XSHE | 2.264671e+11 | -0.097157 | 0.313151 | -0.040068 |
000002.XSHE | 8.983824e+10 | -0.266219 | -0.042926 | -1.541587 |
000063.XSHE | 1.783780e+11 | -0.128866 | 0.068885 | -0.095796 |
000100.XSHE | 8.957622e+10 | -0.065272 | -0.015927 | -1.484159 |
000157.XSHE | 7.185378e+10 | -0.009582 | 0.091424 | 0.299958 |
处理缺失值
#为了计算,先把数据中的空值去掉
factors = factors.dropna()
#检查下是否还有空值
factors.isnull().sum()
标准化、降维
由于市值数据值特别大,会直接影响结果,因此需要进行标准化
#因为各因子数值的量纲差异较大
#需要做一点简单的缩放处理
from sklearn.preprocessing import StandardScaler
#导入scikit-learn中的PCA主成分分析工具
from sklearn.decomposition import PCA
#创建StandardScaler实例,会将数据量纲压缩到同一个区间中
scaler = StandardScaler()
#使用StandardScaler缩放原始的因子值
factors_scl = scaler.fit_transform(factors)
#接下来使用PCA,提取主成分数量指定为1
pca = PCA(n_components = 1)
#使用缩放后的数据进行拟合
pca.fit(factors_scl)
#查看pca给各因子分配的权重
pca.components_
array([[0.17034834509272317, 0.6941037872659619, 0.6899014299868211,
0.11505386012367508]])
最后生成的pca降成一维的值,简单的说,这一列就代表前面4列因子的值
#在factors数据表中添加一个pca字段
#存储提取出来的主成分
factors['pca'] = pca.transform(factors_scl)
#看一下主成分数值最高的5只股票
factors.sort_values(by='pca', ascending = False).head(10)
code | mc | cfp | npr | npgr | pca |
---|---|---|---|---|---|
000617.XSHE | 8.204709e+10 | 0.566187 | 12.261549 | -0.081553 | 13.831990 |
600000.XSHG | 2.976311e+11 | 0.819269 | 0.261202 | 0.128075 | 4.400846 |
601916.XSHG | 7.827421e+10 | 0.839137 | 0.236163 | 0.028149 | 4.366769 |
000166.XSHE | 1.272029e+11 | 0.607792 | 0.228119 | 0.431598 | 3.164233 |
600015.XSHG | 1.142692e+11 | 0.588408 | 0.293962 | 0.038419 | 3.094389 |
300059.XSHE | 3.695396e+11 | 0.169345 | 2.618757 | -0.012045 | 3.090468 |
601169.XSHG | 1.241093e+11 | 0.563349 | 0.379436 | 0.012187 | 3.041558 |
600674.XSHG | 7.316785e+10 | -0.015945 | 3.294503 | 0.126183 | 2.565690 |
601939.XSHG | 2.125093e+12 | 0.256305 | 0.445321 | 0.006780 | 2.485684 |
600036.XSHG | 1.098829e+12 | 0.338387 | 0.443594 | 0.005400 | 2.395596 |
结论
- 最终我们将前面的4个因子,降维【“浓缩”】成一个新的列pca,来代表前面4个因子。
- 因此,pca 值最大,可以认为其代码股票就越好,所以选择pca最大的股票作为我们的标的,完成了选股策略。
- 上述只是一个简单的思路,在实际的实践中,有很多的思路和方法,使用不同的因子或算法,找到最适合自己的投资组合。