股票量化交易SQL特征工程入门

news2024/10/2 14:20:55

虽然现在各种量化教程和自助平台铺天盖地,但是对于新人来说入门最重要的事情就是挖掘特征。

对于传统的学习路径第一步是学习Python或者某一门编程语言,虽说Python入门容易上手快,但是要在实际应用中对股票数据进行分析,并挖掘有用特征还是一件比较麻烦的事情。以一个简单的分析为例,使用从Kaggle下载的日本股市数据(实验使用train_files目录下的stock_prices.csv,或者直接下载CSDN附件):

  1. 计算买入后持有1天,2天的收益
  2. 计算3个指标:5日,10日均价,以及日最高价除以最低价的5日最大值。
  3. 分析上述3个指标以及组合后与收益的关系,寻找潜在可用的特征组合。
  4. 对指标进行挡位划分后对特征做进一步的分析。

直接使用编程语言当然可以解决问题,但是存在以下几个挑战:

  1. 原始数据包含多个交易日多个股票的数据,需要进行拆分。
  2. 针对单个股票数据,计算特征需要自行设计算法管理时间窗口。
  3. 数据挡位划分,特征排列组合需要大量代码实现。
  4. 代码正确性验证困难,维护理解成本高。

相比与Python之类的编程语言,SQL最大的特点是声明式的,你只需要告诉系统你想做什么,具体怎么做系统在后台帮你默默完成。上面我们提到的几个挑战,借助于SQL的OLAP函数我们可以轻松完成。

本教程使用Jupyter Lab编写,大家可以直接安装Anaconda全家桶,也可以安装较小的mini conda,在文章的末尾有相关安装参考教程链接。安装后执行"pip install asqlcell"安装jupyter lab的一个sql插件,本文的Python/SQL混合编程依赖该插件实现。在教程中我们也会顺带穿插讲解一下用到的OLAP函数。

点这里下载测试数据。

首先引入我们需要的包

import asqlcell

使用sql加载数据,直接使用csv文件作为数据源。%%sql代表后面要执行的是SQL语句,%%sql后面跟的名字用于存储SQL语句执行的结果,是pandas的DataFrame类型,可以在Python代码里直接访问使用。

%%sql prices
select RowId as code,
       strftime(Date, '%Y-%m-%d') as date,
       Open as open,
       High as high,
       Low as low,
       Close as close
from stock_prices.csv

执行结果如下:

使用asqlcell插件,在标题栏对数值类型直接展示了一个简单的分布提示,可以点击进行排序,还可以分页查看所有数据。

源数据的code字段是"日期_股票代码"的格式,这部分处理也可用用SQL完成,但是我们使用asqlcell插件的优点就是可用进行混合编程,因为SQL语句返回的数据集是pandas的DataFrame,所以这里就用Python来做个简单的处理。

prices['code'] = prices['code'].str.split('_', expand=True)[1]
prices

查看一下prices,确认后我们在此基础上开始分析处理。

现在插播一下OLAP函数,虽然很多同学都能熟练使用group by和聚合函数求和求平均,但是对OLAP相关函数使用还是比较陌生。

我们有一张如下的销售业绩表,我们需要对每个地区的销售员业绩进行排序。直接用order by是对所有人的业绩排序,如果用group by则可以根据每个地区的业绩汇总(总和,平均...)等进行排序。那怎么对做到根据每个地区分组然后再根据每个组的排序结果给出排名序号呢?

员工地区业绩
Tom上海80
Jack广州90
Marry广州110
Mike上海85
Jeff上海70

现在我们就需要借助OLAP函数了,也被称为窗口函数。因为我们按地区把数据划分为一个个窗口,然后在窗口内做排序。代码如下:

%%sql sales_rank
select *, rank() over (partition by 地区 order by 业绩 desc) as ranking
from data.csv

窗口函数的基本语法规则如下:

<窗口函数> over (partition by <用于分组的列名> order by <用于排序的列名>)

在这里我们根据地区对数据进行分组,按照每个地区的数据根据业绩倒序排序,然后再使用rank函数计算每个窗口内员工的排名。这个功能如果用Python实现,大概分为以下几步:1)通过字典将数据分组;2)实现排序函数;3)根据排序结果填充排名;4)数据合并。显然通过SQL实现效率大大提高了,执行效率在我们后面的例子会体现出来,肯定比你直接用Python实现跑得快。

与GROUP BY相比,窗口函数不影响数据的总行数,只是计算在每个窗口分别进行。

如果窗口需要复用,可以使用window关键字将这部分提取出来:

%%sql sales_rank
select *, rank() over area_window as ranking
from data.csv
window
    area_window as (partition by 地区 order by 业绩 desc)

现在我们进入正题,需求如下,计算以下以下特征:

  1. FA: 5日均线
  2. FB: 10日均线
  3. FC: 日最高价除以最低价的5日最大值
  4. GA1: 当前交易日的下一个交易日开盘价买入,T+1日收盘价卖出的收益
  5. GA2: 当前交易日的下一个交易日开盘价买入,T+2日收盘价卖出的收益

我们使用上面生成的prices数据集,以5日均线这个特征为例。首先是怎么划分窗口,因为数据包含了一段时间内所有股票的数据,所以我们根据股票代码划分窗口,每个窗口代表一个股票,然后再根据日期从远到近排序。因为我们要找的是5日均线,所以用between限制数据范围。"4 preceding and 0 following"代表从4天前到今天总共5天。

代码如下:

%%sql fa
select code,
       date,
       avg(close) over five as fa,
from prices
window
    five as (partition by code order by date asc rows between 4 preceding and 0 following)

230万行数据,1.65秒完成了计算和输出。

下面我们要计算T+1的收益,对于某个股票某一天这条记录来说,就是买入价按后一天的开盘价计算,卖出价按再后一天的收盘价计算。我们直接根据股票代码划分窗口,每个窗口按日期排序就可以。关于如何选择后N天的记录我们可以使用lead函数。如果是前N天则使用lag函数。

%%sql gain
select code,
       date,
       (lead(close, 2, null) over days) / (lead(close, 1, null) over days) - 1 as ga1,
       (lead(close, 3, null) over days) / (lead(close, 1, null) over days) - 1 as ga2
from prices
window
    days as (partition by code order by date asc)

到这里大家可以自己尝试一下解决这两个问题:

  1. 计算”日最高价除以最低价的5日最大值
  2. 可能会存在开盘价格一部拉到位无法买入的情况,如何判断?

下面我们提供完整的SQL语句:

%%sql features
select code,
       date,
       avg(close) over five as FA,
       avg(close) over ten as FB,
       max(high / close) OVER five as FC,
       (lead(low, 1, null) over days) < (lead(high, 1, null) over days) as canBuy,
       (lead(close, 2, null) over days) / (lead(close, 1, null) over days) - 1 as GA1,
       (lead(close, 3, null) over days) / (lead(close, 1, null) over days) - 1 as GA2
from prices
window
    five as (partition by code order by date asc rows between 4 preceding and 0 following),
    ten as (partition by code order by date asc rows between 9 preceding and 0 following),
    days as (partition by code order by date asc)

230万行数据不到3秒就完成了处理。

 下面我们要根据收益的情况对FA/FB/FC三个特征进行分析。分析前必须对以下数据进行过滤:1)收益超过30%的记录(不要相信天上掉馅饼);2)没有买入机会的记录(参考A股就是全体封死涨停板)。经过处理发现大约4万条记录被过滤。

%%sql filtered_features
select *
from features
where GA1 is not null and
      abs(GA1) < 0.30 and
      GA2 is not null and
      abs(GA2) < 0.3 and
      canBuy is not null and
      canBuy

我们做一个简单的相关性分析,及计算特征与收益的相关系数。一般来说,相关系数越大这个特征越值得我们关注,及变化方向趋同更明显。如果是负值则表明变化方向相反。

%%sql corr
select corr(FA, GA1) as ca_1,
       corr(FB, GA1) as cb_1,
       corr(FC, GA2) as cc_1,
       corr(FA, GA2) as ca_2,
       corr(FB, GA2) as cb_2,
       corr(FC, GA2) as cc_2
from filtered_features

在实际工作中,一个特征往往会继续划分,比如收入我们会细分不同的收入层次。这里我们把5日均线这个特征分为几档,查看不同挡位的收益分布。这里我们会发现低挡位的收益表现要好于高档位。

%%sql analysis
SELECT RFA, avg(GA1) * 100 as GA1, avg(GA2) * 100 as GA2, count(1) as rows
from
(
    select cast(percent_rank() over wfa * 5 as int) as RFA,
           ga1, ga2
    from filtered_features
    window
        wfa as (partition by date order by FA)
)
group by RFA

在实际应用中,我们肯定不会只使用一个指标,如果我们想查看所有指标排列组合后的情况呢?cube函数可以帮到你,使用cube(a, b, c)等于生成不同的组合group by然后把结果合并起来。

%%sql cube_analysis
SELECT RFA, RFB, RFC, avg(GA1) * 100 as GA1, avg(GA2) * 100 as GA2, count(1) as rows
from
(
    select cast(percent_rank() over window_fa * 5 as int) as RFA,
           cast(percent_rank() over window_fb * 5 as int) as RFB,
           cast(percent_rank() over window_fc * 5 as int) as RFC,
           ga1, ga2
    from filtered_features
    window
        window_fa as (partition by date order by FA),
        window_fb as (partition by date order by FB),
        window_fc as (partition by date order by FC)
)
group by cube(RFA, RFB, RFC)

我们可以直接筛选一下,找出T+1收益大于0.1%或者T+2收益大于0.2%的记录。对结果排序我们会发现一些好的特征组合。最好的特征居然一天就有接近3%的收益,但是记录条数太少,属于可遇不可求。可以点击rows看下记录最多的组合,收益接近0.1%每天,不考虑手续费年化收益超过20%。争对这种特征,可以考虑试着去做进一步的深入挖掘看是否有实盘机会。

%%sql good_features
select *
from cube_analysis
where (GA1 > 0.1) or (GA2 > 0.2)

相比传统的Python编程,使用SQL语句对数据进行把玩表达能力更强,出错更容易排查,对结果验证也比较方便。大家可以试着在网上下载数据进行实验,练好神功,早日实现财务自由。

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

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

相关文章

【数据库】第一章 绪论

第一章 绪论 1.1 数据库系统概述 数据库课程的学习内容 数据库的4个基本概念&#xff1a; 数据&#xff1a;描述事物的符号记录称为数据。 数据的含义成为数据的语义&#xff0c;数据与其语义是不可分割的。 数据库&#xff1a;数据库是长期存储在计算机内、有组织、可共享的…

Netty核心组件ChannelPipeline事件handler源码解析

源码解析目标 当请求进来&#xff0c;ChannelPipeline如何协调内部这些Handler通过源码梳理ChannelPipeline 与ChannelHandlerContext中的read&#xff0c;fireChannelRead等方法的不同 inbound源码解析 在 Netty启动流程源码剖析 文中我们已经知道&#xff0c;启动后&#…

公司项目vue cli2升级到vue cli3

背景&#xff1a;公司项目历时时间较长&#xff0c;通过长时间的迭代&#xff0c;目前项目文件较多&#xff08;src目录下有2217个文件&#xff09;&#xff0c;系统庞大&#xff0c; 之前通过vue cli2脚手架构建的项目框架&#xff0c;在本地开发时已经明显感觉到吃力&#xf…

Win10+vs2019配置与运行RenderMatch(踩坑记录)

Win10vs2019配置与运行RenderMatch RenderMatch旨在解决aerial images 和ground images 匹配问题&#xff0c;其思路可参考原论文 “Leveraging Photogrammetric Mesh Models for Aerial-Ground Feature Point Matching Toward Integrated 3D Reconstruction” 1.源码下载 G…

【2023new】OAK相机如何将Yolov5转换成blob格式?

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…

机械革命极光Pro电脑开启出现英文代码无法启动怎么办?

机械革命极光Pro电脑开启出现英文代码无法启动怎么办&#xff1f;有的小伙伴在使用机械革命极光Pro电脑的时候&#xff0c;正常开启电脑却无法进入到桌面中&#xff0c;而是显示一些英文错误提示。遇到这个问题是我们的系统故障了&#xff0c;可以通过U盘重装系统的方法来进行问…

logback 自定义日志输出到数据库

项目日志格式 Spring Boot 的默认日志输出类似于以下示例&#xff1a; 2021-12-14 22:40:14.159 INFO 20132 --- [ main] com.kuangstudy.SpringbootApplication : Started SpringbootApplication in 2.466 seconds (JVM running for 3.617)输出以下项目&…

SpringBoot 整合 MongoDB 6 以上版本副本集及配置 SSL / TLS 协议

续上一篇 Linux 中使用 docker-compose 部署 MongoDB 6 以上版本副本集及配置 SSL / TLS 协议 前提&#xff1a;此篇文章是对上一篇文章的实战和项目中相关配置的使用&#xff0c;我这边针对 MongoDB 原有基础上做了增强&#xff0c;简化了 MongoDB 配置 SSL / TLS 协议上的支…

Android Studio引入JNI第三方库

一、前言 JNI作为Java与native沟通的桥梁&#xff0c;项目开发中难免要使用到&#xff1b;而我们除了自己开发JNI之外&#xff0c;有时候还要在Android Studio引入别人开源的C第三方库&#xff0c;并在jni层实现第三方库的调用。 二、流程 1.导入头文件和实现文件 将第三方…

Linux内核进程地址空间与进程内存布局

一&#xff0c;进程空间分布概述 对于一个进程&#xff0c;其空间分布如下图所示&#xff1a; 程序段(Text):程序代码在内存中的映射&#xff0c;存放函数体的二进制代码。初始化过的数据(Data):在程序运行初已经对变量进行初始化的数据。未初始化过的数据(BSS):在程序运行初未…

九龙证券|券商春季策略扎堆来袭 风格切换成焦点

2月以来&#xff0c;国泰君安、中信建投、国金证券等10余家券商组织相继发布2023年春季战略。综合来看&#xff0c;组织对A股持达观预期&#xff0c;未来两三个月A股商场或迎来重要切换。风格上&#xff0c;“中心财物&#xff0c;生长接力”或许成为上半年装备主线&#xff0c…

java 系列之Mybatis

java 系列文章 文章目录java 系列文章前言一、Mybatis 入门1.1 认识 框架&#xff08;了解&#xff09;1.2 认识 ORM&#xff08;要知道&#xff09;1.3 认识 Mybatis&#xff08;要知道&#xff09;二、Mybatis 使用2.1 创建maven项目并导入依赖2.2 准备数据库&#xff0c;包和…

释放内存流程

你好&#xff0c;我是安然无虞。 thread cache回收内存 当从 thread cache 中申请的内存对象使用完毕需要还回来的时候, 只需要计算出该内存对象对应 thread cache 中的哪一个自由链表桶, 然后将该内存对象插入进去即可. 不过需要注意的是, 如果不断有内存对象释放回来, 那么…

Java实现根据拼音首字母的排序

1.项目 手机APP端要对企业列表按企业名称首字母(如果企业名是英文的就按)进行分类排序&#xff0c;效果如下&#xff1a; 2.实现过程 2.1 首先引入项目的pinyin4j-2.5.0.jar包。 这个jar的下载地址如下&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1hkP_gGAYcgzyK_D…

跨链桥:Web3黑客必争之地

跨链桥&#xff0c;区块链的基础设施之一&#xff0c;所实现的功能是允许用户将自己的资产从一条链转移至另外一条链上&#xff0c;是连接不同的区块链的关键桥梁&#xff0c;常使用中心化的方式进行实现。由于跨链桥自身往往存储有用户所质押的巨额资产&#xff0c;是Web3黑客…

KUKA KR C4机器人与S7-1200PLC进行PROFINET通信的具体方法和步骤

KUKA KR C4机器人与S7-1200PLC进行PROFINET通信的具体方法和步骤 首先,从KUKA机器人控制柜中将KOP备选软件包拷贝出来,然后在“WorkVisual Development Environment”安装KUKA备选软件包(版本非常重要,尽量从控制柜中拷贝), 也可以从以下链接中获取: KUKA机器人PROFINET…

php 任务调度

在日常开发中&#xff0c;我们总会遇到一些在某个指定的时刻去执行&#xff0c;或是每隔xx时间执行&#xff0c;或是需要一直在后台监听的任务执行。基于这个需求&#xff0c;对于php我找了一些办法来实现这些功能 1、依赖于laravel的任务调度。 每隔xx时间执行一次命令&#…

七、虚拟机栈

虚拟机栈出现的背景 1.由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的&#xff0c;不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。 2.优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同…

XC7K70T-1FBG676I【FPGA】XC3S200-4FTG256C参数XC7K70T-2FBG676C

Kintex-7 FPGA为快速增长应用和无线通信提供最优性价比和低功耗。Kintex-7 FPGA允许设计人员构建卓越带宽和12位数字可编程模拟&#xff0c;同时满足成本和功耗要求。Kintex-7内置支持8通道PCI Express (Gen1/Gen2)&#xff0c;用于连接主机系统。7系列器件利用Xilinx统一架构保…

如何实现外网跨网远程控制内网计算机?快解析来解决

远程控制&#xff0c;是指管理人员在异地通过计算机网络异地拨号或双方都接入Internet等手段&#xff0c;连通需被控制的计算机&#xff0c;将被控计算机的桌面环境显示到自己的计算机上&#xff0c;通过本地计算机对远方计算机进行配置、软件安装程序、修改等工作。通俗来讲&a…