量化投资学习笔记87——实现量化交易经典策略:多因子选股(改进2)

接着上次的程序,我想到用机器学习算法来进行多因子选股的方法。之前的程序是先假设因子之间是线性关系,然后求方程的系数。现在我考虑能不能将每个候选股票的各个因子的值,以及每个股票的年化收益率直接当做数据”喂给”算法,看看能有啥结果。
首先造数据吧。
因子数据的获取跟筛选和之前是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 获取股票数据,进行初步筛选,返回供因子分析的股票数据。
def getFactors():
# data = ts.get_stock_basics()
# print(data.head())
# print(len(data))
# data.to_csv("stocks.csv")
data = pd.read_csv("stocks.csv", index_col = "code")
# 排除亏损的股票
data = data[data.npr > 0.0]
# 排除上市不满2年的
data = data[data.timeToMarket <= 20180801]
# 排除ST股票
data = data[~ data.name.str.contains("ST")]
# 排除代码小于100000的股票
data = data[data.index >= 100000]
# 排除退市的股票
data = data[data.pe != 0]
# print(data)
return data

接下来,要对每只股票在回测时间范围内回测出其年化收益率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 对所有股票回测其年化收益率
def getReturn(data):
if os.path.exists("data.csv"):
data = pd.read_csv("data.csv", index_col = "code")
return data
start = "2017-01-01"
end = "2020-07-31"
codes = data.index
names = fromCodeToName(data, codes)
codes = [str(x) for x in codes]
# print(codes)
# print(names)
# 在数据中增加一列计算年化收益率
data["ar"] = 0.0
t = 0
cash = 100000
for code in data.index:
test = backtest.BackTest(FactorStrategy, start, end, [str(code)], [names[t]], cash, bDraw = False)
result = test.run()
print("第{}次回测,股票代码{},回测年化收益率{}。".format(t+1, code, result.年化收益率))
data.loc[code, ["ar"]] = result.年化收益率
t += 1
data.to_csv("data.csv")
return data

只用回测一次,保存到文件里,下次直接读取。
现在数据有了,开始分析吧。
先看看数据情况:

1
print(data.info())


先看看年化收益率的分布情况。

下面用pairplot将所有变量两两配对画图看看。

先用线性回归,把所有变量放进去,以年化收益率为因变量,看看回归结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 多元线性回归
def multiRegress(data):
x = data.iloc[:, 3:21]
y = data.iloc[:, 22]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state = 631)
line_reg = LinearRegression()
model = line_reg.fit(x_train, y_train)
print("模型参数:", model)
print("模型截距:", model.intercept_)
print("参数权重:", model.coef_)

y_pred = line_reg.predict(x_test)
sum_mean = 0
for i in range(len(y_pred)):
sum_mean += (y_pred[i] - y_test.values[i]) ** 2
sum_erro = np.sqrt(sum_mean /len(y_pred))
print("RMSR=", sum_erro)
print("Score=", model.score(x_test, y_test))
# ROC曲线
plt.figure()
plt.plot(range(len(y_pred)), y_pred, 'b', label="predict")
plt.plot(range(len(y_pred)), y_test, 'r', label="test")
plt.legend(loc="upper right")
# 显示图中的标签
plt.xlabel("facts")
plt.ylabel('ar')
plt.savefig("line_regress_result.png")
plt.close()
# 保存模型
joblib.dump(model, "LineRegress.m")
return mode

回归结果

1
2
模型参数: LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)                              模型截距: -0.018973640684523358                参数权重: [-1.56402370e-06  1.74669993e-07  6.98664492e-07 -4.44613478e-08  5.43375427e-07  3.22291009e-06  2.06702726e-05  5.75364273e-03  6.13467186e-03 -3.06546497e-03 3.31053106e-03  5.14874613e-10              1.80572770e-07  3.25824964e-03  4.28603067e-05  8.08821164e-07                  1.29863432e-04  5.30351262e-06]                          
RMSR= 0.019172719955231714 Score= 0.4389434373773129


用回归结果的预测值排序取前十的股票作为组合中的股票,回测10年的结果看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 测试多元线性回归的效果
def testMultiRegress(data):
model = joblib.load("LineRegress.m")
pred_return = model.predict(data.iloc[:, 3:21])
# print(pred_return)
data["pred_ar"] = pred_return
# print(data)
# 排序
data = data.sort_values(by = "pred_ar", ascending = False)
# print(data)
# 取前十个股票作为投资标的
codes = data.index[0:10].values
# print(codes)
names = fromCodeToName(data, codes)
codes = [str(x) for x in codes]
start = "2010-01-01"
end = "2020-07-01"
cash = 1000000
opttest = backtest.BackTest(FactorStrategy, start, end, codes, names, cash, bDraw = True)
result = opttest.run()
print("多元线性回归的回测结果:")
print(result)

回测结果

年化收益率41.8%。

还不错,但还是有那个bug,我是用最近的因子数据算的,回测的数据却是用之前的数据。
再试一下别的机器学习算法。
多项式回归,代码不赘述了,直接放结果。


回归结果个别值相差很大,最后回测结果也差一些。不过年化仍然达到40%。
在写这篇笔记搜索的时候我才知道,原来随机森林和深度学习也可以做回归!试试吧。
先用回归随机森林,参考这里: https://blog.csdn.net/GitzLiu/article/details/81952712

看着还不错,再用结果回测试试。

更好一些,年化到43.4%。
具体代码看: https://github.com/zwdnet/MyQuant/tree/master/48
策略文件为facts.py。
这次主要是回归算法。下次看分类算法。

我发文章的三个地方,欢迎大家在朋友圈等地方分享,欢迎点“在看”。
我的个人博客地址:https://zwdnet.github.io
我的知乎文章地址: https://www.zhihu.com/people/zhao-you-min/posts
我的微信个人订阅号:赵瑜敏的口腔医学学习园地

欢迎打赏!感谢支持!