Python量化交易05——基于多因子选择和选股策略(随机森林,LGBM)

news2024/9/26 1:15:29

  参考书目:深入浅出Python量化交易实战


在机器学习里面的X叫做特征变量,在统计学里面叫做协变量也叫自变量,在量化投资里面则叫做因子,所谓多因子就是有很多的特征变量。

本次带来的就是多因子模型,并且使用的是机器学习的强大的非线性模型,集成学习里面的随机森林和LGBM模型,带来因子的选择策略和股票的选择策略。


由于股票数据的获取都需要第三方库或者是专业的量化投资框架,很多第三方库某些功能需要收费(Tushare),而免费的一些库(证券宝)获取的数据特征变量又没那么多。所以这里是用聚宽量化投资框架,是可以免费使用一些功能的(只需要注册一个账号)。这里获取数据就采用聚宽平台的功能了。


数据获取

本次使用沪深300作为股票池,选获取一些财务指标:

#创建query对象,指定获取股票的代码、市值、净运营资本
#净债务、产权比率、股东权益比率、营收增长率、换手率、
#市盈率(PE)、市净率(PB)、市销率(PS)、总资产收益率因子

#还是先导入jqdata和技术分析工具
import jqdata
from jqlib.technical_analysis import *
#同样选择沪深300成分股做股票池
stocks = get_index_stocks('000300.XSHG')

q = query(valuation.code, valuation.market_cap,
           balance.total_current_assets- balance.total_current_liability,
        balance.total_liability- balance.total_assets,
           balance.total_liability/balance.equities_parent_company_owners,
        (balance.total_assets-balance.total_current_assets)/balance.total_assets,
        balance.equities_parent_company_owners/balance.total_assets,
        indicator.inc_total_revenue_year_on_year,
           valuation.turnover_ratio,
        valuation.pe_ratio,
           valuation.pb_ratio,
           valuation.ps_ratio,indicator.roa).filter(
     valuation.code.in_(stocks))
#将获得的因子值存入一个数据表
df = get_fundamentals(q, date = None)
#把数据表的字段名指定为对应的因子名
df.columns = ['code', '市值', '净营运资本', 
              '净债务', '产权比率','非流动资产比率',
              '股东权益比率', '营收增长率'
          ,'换手率','PE','PB','PS','总资产收益率']
#检查结果
df.head()

 需要在聚宽的环境才能获得上面的数据,本地Python是出不来的。

设置一下股票代码作为索引,获取一些时间格式。

#将股票代码作为数据表的index
df.index = df.code.values
#使用del也可以删除列
del df['code']
#下面来把时间变量都定义好
today = datetime.datetime.today()
#设定3个时间差,分别是50天,1天和2天
delta50 = datetime.timedelta(days=50)
delta1 = datetime.timedelta(days=1)
delta2 = datetime.timedelta(days=2)
#50日前作为一个历史节点
history = today - delta50
#再计算昨天和2天前的日期
yesterday = today - delta1
two_days_ago = today - delta2

再然后获取一些技术指标数据:

#下面就获取股票的动量线、成交量、累计能量线、平均差、
#指数移动平均、移动平均、乖离率等因子
#时间范围都设为10天


df['动量线']=list(MTM(df.index, two_days_ago, timeperiod=10, unit = '1d', 
                   include_now = True, fq_ref_date = None).values())

df['成交量']=list(VOL(df.index, two_days_ago, M1=10 ,unit = '1d', include_now = True, 
                   fq_ref_date = None)[0].values())

df['累计能量线']=list(OBV(df.index,check_date=two_days_ago, timeperiod=10).values())

df['平均差']=list(DMA(df.index, two_days_ago, N1 = 10, unit = '1d', include_now = True, 
                   fq_ref_date = None)[0].values())

df['指数移动平均']=list(EMA(df.index, two_days_ago, timeperiod=10, unit = '1d', include_now = True, 
                      fq_ref_date = None).values())

df['移动平均']=list(MA(df.index, two_days_ago, timeperiod=10, unit = '1d', include_now = True, 
                   fq_ref_date = None).values())

df['乖离率']=list(BIAS(df.index,two_days_ago, N1=10, unit = '1d', include_now = True, 
                    fq_ref_date = None)[0].values())
#把数据表中的空值用0来代替
df.fillna(0,inplace=True)
#检查是否成功
df.head()

这样就获得了很多X,即特征变量,即因子。

下面构建y,我们的响应变量是一个分类的变量,即是否获得了超过市场的平均回报的收益率,是的话为1,不是为0 。

这里使用前一日的收盘价除以前50天的收盘价 然后减去1,作为收益率的值,计算出那些收益率大于均值的样本股则y为1 ,否则为0 。

#获取股票前一日的收盘价
df['close1']=list(get_price(stocks, end_date=yesterday, 
                       count = 1,fq='pre',panel=False)['close'])
#获取股票50日前的收盘价
df['close2']=list(get_price(stocks,  end_date=history, 
                       count = 1,fq ='pre',panel=False)['close'])

#计算出收益
df['return']=df['close1']/df['close2']-1
#如果收益大于平均水平,则标记为1
#否则标记为0
df['signal']=np.where(df['return']<df['return'].mean(),0,1)
#检查是否成功
df.head()

 可以看到最后一列是我们的响应变量y。


模型构建

将X和y都准备好。划分训练集和测试集,导入随机森林分类器。

#导入数据集拆分工具
from sklearn.model_selection import train_test_split
#导入随机森林分类器
from sklearn.ensemble import RandomForestClassifier
#把因子值作为样本的特征,所以要去掉刚刚添加的几个字段
X = df.drop(['close1', 'close2', 'return', 'signal'], axis = 1)
#把signal作为分类标签
y = df['signal']
#将数据拆分为训练集和验证集
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size = 0.2)
#创建随机森林分类器实例,指定random_state便于复现
clf = RandomForestClassifier(n_estimators=5000,random_state=100)
#拟合训练集数据
clf.fit(X_train, y_train)
#查看分类器在训练集和验证集中的准确率
print(clf.score(X_train, y_train),
      clf.score(X_test, y_test))

 

分类问题,所以采用随机森林分类器,然后进行拟合和评价。

可以看到在训练集上的准确率为100%,在测试集删高的准确率为0.9333,说明模型的拟合效果很不错。


 

 因子重要性

接下来使用随机森林的变量的重要性排序,原理是基础学习器决策树分裂时,若一个变量分裂时让损失函数下降得越多,说明这个变量越重要。

#为了便于观察,我们创建一个数据表
#数据表有两个字段,分别是特征名和重要性
#特征名就是因子的名称


factor_weight = pd.DataFrame({'features':list(X.columns),
                             'importance':clf.feature_importances_}).sort_values(
    #这里根据重要程度降序排列,一遍遍找到重要性最高的特征
    by='importance', ascending = False)
#检查结果
factor_weight

 可以看到最重要的变量是技术指标平均差。这也是肯定的,因为平均差里面包含了过去和现在的股价信息最多,和我们的响应变量最为相似。

画图更加直观的查看变量重要性排序。

import seaborn as sns
plt.figure(figsize=(6,4),dpi=128)
sns.barplot(y=factor_weight['features'],x=factor_weight['importance'],orient="h")
plt.xlabel('重要程度')
plt.ylabel('因子名称')
plt.xticks(fontsize=10,rotation=35)
plt.title("因子重要性对比")
plt.show()

 和上面结论一样,技术指标平均差对我们的响应变量是否获得超额回报的影响最大,然后是公司本身的财务指标,营业收入增长率,净收运营资本等。


选股策略

接下来我们使用对于表格数据最强的机器学习方法,轻量梯度提升方法——LGBM模型,对我们的股票市值进行预测,然后选取实际值和预测值差距最大的股票作为选股策略。即选取价值被低估的股票。

此时y是股票市值,X是前面那些财务技术指标

X=df.iloc[:,1:-3]
y=df.iloc[:,0]

构建回归器

from lightgbm import LGBMRegressor
model = LGBMRegressor(n_estimators=100,objective='regression', random_state=0)
model.fit(X, y)
model.score(X, y)

 整体模型的拟合优度为86%,还不错。

用真实值减去预测值,然后进行排序,算的找出前10 的被低估的公司

diff = pd.DataFrame(np.array(y)-model.predict(X), index = y.index, columns = ['预测值和真实值的差值'])
#将该数据表中的值,按生序进行排列
diff = diff.sort_values(by = '预测值和真实值的差值', ascending = True)
#找到市值被低估最多的10只股票
diff.head(10)

 前十都是被低估了,负得越多说明被低估的越厉害。


 受限于框架的使用,该策略我本人没有进行回测检验其收益率。书上的收益率大概是年化6%,不高,但是也算不错了。

(本案例仅作为策略研究,不构成任何投资意见。)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/127279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统下的组管理和权限管理

Linux系统下的组管理和权限管理 组管理 在linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在linux中每个文件有所有者、所在组、其它组的概念。 对于一个文件而言&#xff0c;有以下几种说法&#xff1a;1)所有者&#xff1b;2)所在组&#xff1b;3)其它组&#…

TypeScript中的类 Class

公共属性的修饰符&#xff1a; public&#xff1a;公共&#xff0c;默认修饰符&#xff0c;外部和内部都能使用private&#xff1a;私有的&#xff0c;只能内部类用&#xff0c;外部不能读写protected&#xff1a;当前类和派生类(子类)可访问readonly:外部只能读不能写static&…

快速上手 Docker 最新 WebAssembly 技术预览版

本文为译文&#xff0c;原文见&#xff1a;https://nigelpoulton.com/getting-started-with-docker-and-wasm/ 轻松体验 Docker 和 Wasm ——编写一个应用&#xff0c;将其编译为 Wasm&#xff0c;将其打包为 OCI 镜像&#xff0c;将之存储在 Docker Hub 中&#xff0c;使用 Do…

4 JMeter 参数化常用方式

文章目录2.4 JMeter 参数化常用方式2.4.1 用户定义的变量2.4.2 用户参数2.4.3 CSV数据文件设置 CSV Data Set Config2.4.4 函数(_counter)2.4 JMeter 参数化常用方式 2.4.1 用户定义的变量 应用场景&#xff1a;全局参数 添加方式&#xff1a; 测试计划->线程组->配置…

【AcWing每日一题】4366. 上课睡觉

有 N 堆石子&#xff0c;每堆的石子数量分别为 a1,a2,…,aN。 你可以对石子堆进行合并操作&#xff0c;将两个相邻的石子堆合并为一个石子堆&#xff0c;例如&#xff0c;如果 a[1,2,3,4,5]&#xff0c;合并第 2,3 堆石子&#xff0c;则石子堆集合变为 a[1,5,4,5]。 我们希望…

LeetCodeday02

977.有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&#xff1a;平方后&am…

Java开发学习(三十五)----SpringBoot快速入门及起步依赖解析

一、SpringBoot简介 SpringBoot 是由 Pivotal 团队提供的全新框架&#xff0c;其设计目的是用来简化 Spring 应用的初始搭建以及开发过程。 使用了 Spring 框架后已经简化了我们的开发。而 SpringBoot 又是对 Spring 开发进行简化的&#xff0c;可想而知 SpringBoot 使用的简…

做报表要用什么插件?

Excel 作为大家最熟悉的报表工具&#xff0c;很多表哥表姐每天都在使用&#xff0c;为了加强 Excel 的报表功能&#xff0c;市面上有非常多的 Excel 增强插件&#xff0c;为 Excel 增加了千奇百怪的能力。今天给大家介绍一款专门用来做中国式复杂报表的Excel 插件&#xff1a;思…

【不一样的递归大法】

&#x1f381;递归&#x1f385;递归&#x1f98c;定义&#x1f385;何时用递归&#xff1a;递归三板斧&#x1f98c;递归递归&#x1f98c;递归大法&#xff1a;三板斧&#x1f385;如何快速写出递归函数&#xff1a;宏观的角度&#x1f385;解题突破&#x1f98c;整数序列相关…

一文了解什么是NFT

一、什么是NFT NFT 是我们可以用来代表独特物品所有权的代币。他们让我们对艺术品、收藏品甚至房地产等事物进行代币化。资产的所有权由以太坊区块链保护——没有人可以修改所有权记录或复制/粘贴新的 NFT。 NFT 代表不可替代的代币。Non-fungible 是一个经济学术语&#xff…

伪操作和混合汇编

目录 一、伪操作: 二、C和汇编的混合编程 三、ATPCS协议(ARM-THUMB Procedure Call Standard) 一、伪操作: 不会生成代码&#xff0c;只是在编译之前告诉编译器怎么编译 GNU的伪操作一般都以‘.’开头 .global symbol 将symbo…

WEB 安全,浅谈 XSS 攻击(附简单实例)

什么是 XSS XSS(Cross-Site-Scripting)&#xff0c;跨站脚本攻击&#xff0c;因为缩写和 CSS 重叠&#xff0c;被别人抢先了&#xff0c;所以只能叫做 XSS。 攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。若受害者运行这些恶意代码&#xff0c;攻击者就可以突破网站…

电感和磁珠有哪些区别?

由于电感和磁珠&#xff0c;很多人会容易认错&#xff0c;本期内容就讲讲&#xff0c;有哪些相似之处&#xff01; 磁珠与电感不仅在外形上相似&#xff0c;他们在功能上也存在很多相似之处&#xff0c;甚至有时候磁珠和电感可以相互代替。但是磁珠与电感也不完全等同&#xff…

开关电源环路稳定性分析(09)——环路补偿六步法

大家好&#xff0c;这里是大话硬件。 我们来回顾一下前面8讲的内容&#xff0c;主要对下面的知识点进行了分析&#xff1a; 系统框图 反馈环节传递函数 功率级传递函数 PWM级传递函数 传递函数计算 如果我们把开关电源看成是不同的电路模块拼接而成&#xff0c;现在已经知…

c++模板认识以及使用

我们都知道c有函数重载的概念&#xff0c;比如我们写一个相加的函数&#xff0c;以整数为例&#xff0c;我们大概率是这样写&#xff1a; int Add(int x,int y) {return xy; } 并且我们知道c函数重载的概念&#xff0c;于是我们若是想写double类型&#xff0c;float类型&…

centos7安装php7.1 验证码GD库扩展

php安装不推荐使用源码包安装&#xff0c;版本太多。线上从php5.x一直升级到php7.1 程序可兼容 配置yum源 rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm 添加epel…

使用Stabel Diffusion

Stabel Diffusion是由CompVis、stabel AI和LAION的研究人员和工程师创建的文本到图像的潜在扩散模型。它由来自LAION-5B数据库子集的512x512图像进行训练。LAION-5B是目前最大的、可自由访问的多模态数据集。 在这篇文章中&#xff0c;将介绍如何使用diffusion库实现Stabel Di…

MAC安装jmeter以及JDK配置

JDK配置 一、检查是否安装了jdk 打开终端&#xff0c;输入java -version校验jdk是否安装 我这已经安装了版本1.8.0版本的 若没有安装&#xff0c;则去官网下载jdk并安装 1、jdk下载官网&#xff1a;Java Downloads | Oracle tar包或者dmg&#xff0c;二者区别在于&#xff1a;…

7、GPIO输入按键检测(外部中断)

目录 0x01、简介 0x001、EXTI 简介 0x002、EXTI 功能框图 0x003、中断/事件线 0x02、硬件设计 0x03、相关库函数 0x0001、外部中断初始化 0x0002、外部中断GPIO引脚选择 0x04、编写函数 0x001、按键外部中断初始化 0x002、中断函数 0x05、源程序下载地址 0x01、简介…

Android插件化换肤原理—— 布局加载过程、View创建流程、Resources 浅析

前言 继上次 WebView 干货分享后&#xff0c;本次将分享下自己在探索学习 App 换肤功能过程中的相关知识&#xff0c;着重分享换肤的原理以及实现思路。 由于篇幅原因分为两篇博客&#xff0c;本文主要分析了 Android 布局加载流程&#xff0c;下一篇将具体讲解插件化换肤实现…