JPX Tokyo Stock Exchange Prediction (1) - Introduction & Baseline
Jie Cao 炼丹师

JPX 东京股票交易预测 JPX Tokyo Stock Exchange Prediction

image

JPX Tokyo Stock Exchange Prediction 比赛是由日本最大的交易所集团JPX麾下东京证券交易所和金融科技公司 AlpacaJapan 联合举办的股票收益预测赛事。JPX 是整合了日本最大的两家交易所——东京证券交易所和大版证券交易所——的交易所集团,在整个日本金融市场是一个巨无霸的存在,几乎整个日本的证券交易业务都是在这两大交易所中进行,其在日本乃至世界金融市场中都有着重要地位。

image

AlpacaJapan 是日本一家金融科技公司,专注于利用人工智能和大数据技术为金融市场提供创新解决方案,通过现代 API 提供加密货币和股票投资、实时市场数据以及端到端的经纪基础设施。这样两大机构联合举办的本次比赛,旨在推动利用数据科学的技术解决证券业务(尤其是股票收益预测)中的问题。因此本次比赛是一个十分切合当前研究和就业热点并且创新极高的比赛。

image

股票是最广为人知的一种金融工具,无数学术研究者、个人投资者以及专业从业者都有利用更先进的方法和技术手段在股票市场上获取巨大利益的梦想。

在传统的股票交易中,投资者何时买入、何时卖出股票通常依赖于专业人员的专业知识、对股票背后企业的深入调研以及多年的交易经验等因素。这种以人类经验和直觉为依据的交易方式被称为主观投资。主观投资虽然灵活,但也存在一些明显的局限性:

  1. 复杂性高:需要考虑众多因素,如宏观经济(经济周期)、行业趋势(猪周期)、公司运营情况(财务表现)等;
  2. 覆盖面有限:人力资源有限,难以覆盖市场上不断增多的股票和庞大的数据;
  3. 难以复制:主观投资的决策更多依赖个人经验和感觉,因此优秀的交易策略难以标准化和复制。

为了克服这些不足,一种新的交易方式应运而生:基于数量化方法的决策——量化投资。量化投资采用数学、统计和计算机科学等工具来分析市场数据,制定并执行交易策略。这种方式具有以下显著优势:

  1. 科学决策:将复杂的投资因素量化为数据,通过数据挖掘和分析,降低人为干扰;
  2. 覆盖面广:可以自动分析大量股票和数据,确保市场的广泛覆盖;
  3. 可复制和执行:策略模型清晰、可量化,具有严格执行和可复制的特性。

因此,量化投资不仅为金融市场带来了更高效、更科学的交易方式,还为投资者提供了一种全新的、基于数据的投资视角和方法。

不断深入探索机器学习与股票收益预测的结合对金融机构和个人职业发展都具有重要意义。金融机构需要稳定、可复现的优秀策略来管理庞大资金,并实现良好收益;而对于个人而言,量化岗位近年来备受青睐,对于具备技术背景并有志于进军金融领域的小伙伴来说,JPX股票收益预测赛题案例是一扇开启这一职业道路的大门。

结合机器学习对股票收益进行预测并开发策略模型已成为金融领域的热门研究方向。本赛题设立了一个典型的运用机器学习算法构建量化策略的场景,涵盖了预测股票收益时所面临的诸多挑战和难题。参赛者需根据自己的模型,从2000支股票中筛选符合预测条件的股票构建投资组合。具体而言,每位参与者将股票按照预期收益从高到低进行排名,并根据前200只股票和后200只股票之间的收益差进行评估。

  • 数据来源:东京证券交易所提供的日本股票市场真实交易数据用于数据竞赛。
  • 具体表单:stock_prices.csv是核心数据文件,包含了每支股票日频的交易价格、收益率等信息,其他5个csv分别是股票的期权、非核心股票信息、周交易量汇总、季度营收财报、股票行业等信息。
  • 数据量级:核心股价数据集为百万级
  • 预测类型:回归问题,预测每支股票在每个时间点上的收益并进行排序

通过参与此竞赛,参赛者不仅能够锻炼自己的数据处理和机器学习技能,还能深入了解量化投资的实际运作,掌握构建有效投资组合的基本方法。这将为那些想要进入金融行业的小伙伴提供宝贵的实践机会。

目录

一、赛题解读

  1. 认识 JPX 股价收益预测赛题

  2. 认识 JPX 的比赛形式和比赛数据

二、时序特征工程

  1. 多维时序特征分析Multi-Dimensional Temporal Feature Analysis

    1.1 在线导入自定义库

    1.2 趋势分析特征

      1.2.1 历史平移和增长率特征

      1.2.2 窗口统计特征

      1.2.3 指数加权移动(EWMA)

    1.3 市场动态特征

  2. 行业分段建模 Industry Segmented Modeling

  3. 模型融合与提交

三、金融量价特征工程

  1. 金融量价指标

一、赛题解读与baseline的建立

1. 认识JPX股价收益预测赛题

JPX股价预测赛题是一个富有挑战性的竞赛,它要求参赛者运用机器学习算法对规定的股票池中2000支股票的未来收益率进行精准预测,并基于这些预测构建投资组合。在这个过程中,模型的优劣将通过投资组合的收益来衡量。因此,我们必须对以下几个核心问题有明确和清晰的认知和了解:

  • 什么是股票池?股票池中2000支股票有什么特点?
  • 什么是股票的收益率、未来收益率?
  • 投资组合是什么?如何构建?
  • 投资组合最终的收益如何衡量?

1.1 股票池与市值的重要性

股票池的定义和特点

股票池是投资者根据一定的标准和条件筛选出的股票集合。通过对这些股票的深入研究和分析,投资者可以寻找到有潜力的投资机会。股票池的构建可以基于许多因素,如行业、市值、财务指标等,不同的投资者或金融机构可以根据自己的投资策略来构建适合的股票池。

在JPX赛题中,股票池的构建有特定的规则。它包括市值排名前2000的股票,而且这些股票必须在交易所上市至少一年。这其中的市值(Market Capitalization)是一个关键概念,也称为市场价值或市值总额。市值反映了公司在股票市场上的总体价值,是衡量公司规模、稳定性和市场认可度的重要指标。简单来说,市值是公司的当前股价与其总发行股数的乘积。例如,我们不能仅仅因为股票1301的股价比1332高就认为1301的市值比1332更高。

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
32
# 导入必要的库
import pandas as pd
import matplotlib.pyplot as plt
import warnings
# 屏蔽警告
warnings.filterwarnings('ignore')
# 设置文件路径
# 如果和我一样将此文件放在和train_files文件夹同一个目录下则不需要额外设置base_dir
base_dir = "./"
train_files_dir = f"{base_dir}/train_files"

# 读取股票价格数据
df_stock_prices = pd.read_csv(f"{train_files_dir}/stock_prices.csv")

# 选择单只股票的数据
single_stock = df_stock_prices[df_stock_prices['SecuritiesCode'] == 1301]
stock_1332 = df_stock_prices[df_stock_prices['SecuritiesCode'] == 1332]


# 绘制单只股票的'Close'价格的时间序列图
plt.figure(figsize=(14, 7))
# 横坐标为日期,纵坐标为收盘价close
# 因为当前数据集的'Date'字段是object类型,所以我们需要转化为datetime类型
plt.plot(pd.to_datetime(single_stock['Date']), single_stock['Close'],label='股票1301')
plt.plot(pd.to_datetime(stock_1332['Date']), stock_1332['Close'],label='股票1332')
plt.rcParams['font.family'] = 'sans-serif' # 显示负号
plt.rcParams['font.sans-serif'] = 'SimHei' # 显示中文
plt.xlabel('日期')
plt.ylabel('收盘价')
plt.title('股票1301的收盘价随时间的变化')
plt.legend()
plt.show()

![png](JPX Tokyo Stock Exchange Prediction\output_23_0.png)

市值的分析和重要性

市值作为公司的一个核心属性,在投资决策中具有重要意义。市值较大的股票或企业通常具有以下特点:

  1. 规模较大、经营稳定:大市值公司通常有着良好的经营历史和市场地位,能够在市场波动中保持相对稳定,降低投资风险。
  2. 资源丰富、竞争力强:大市值公司拥有丰富的资源和强大的竞争力,如优秀的人才、先进的技术、广阔的市场渠道等,这有助于公司在激烈的市场竞争中保持领先地位。
  3. 投资安全性较高:大市值股票因其市场认可度高和业务稳定,通常被视为相对安全的投资对象,适合风险厌恶的投资者。
  4. 流动性好、信息透明:大市值股票在市场上交易活跃,流动性好,使得买卖更加便捷。同时,这些股票通常有着良好的信息披露机制,投资者更容易获得相关的投资信息和分析报告。

了解股票池和市值的重要性是理解JPX股价预测赛题的第一步。在此基础上,我们还需要深入探讨股票收益率、投资组合构建和收益衡量等核心问题。

1.2 股票收益率的计算与理解

股票收益率不仅是反映股票价格变动的重要指标,更是投资者衡量投资效果和风险的关键依据。正向收益率意味着投资盈利,负向收益率则可能带来投资亏损。

在JPX赛题中,理解股票收益率的计算和实际意义是至关重要的一环,因为数据集中的“Target”标签本身就是股票收益率。下面,我们将深入解释这一概念,并探讨如何将其与赛题的实际需求相连接。

1.2.1 股票收益率的计算公式

股票收益率是衡量股票价格变动的指标,其计算方式如下:

              
r(k,t)=C(k,t)C(k,t1)C(k,t1)r_{(k, t)} = \frac{C_{(k, t)} - C_{(k, t-1)}}{C_{(k, t-1)}}

其中,重要因素包括:

  • 股票标的kk,即计算对象。

  • 时间tt,体现买卖行为的先后顺序。

  • 买卖价格CC,分别表示买入和卖出时的价格。

然而,这一公式反映的是已经交易完成后的历史数据,对于如何选择股票并没有实际指导作用。因此,我们需要预测股票未来的收益率,并将预测收益率为正的股票纳入投资组合。因此在JPX赛题中,未来收益率的计算为:

              
r(k,tpre)=C(k,t+2)C(k,t+1)C(k,t+1)r_{(k, t_{pre})} = \frac{C_{(k, t+2)} - C_{(k, t+1)}}{C_{(k, t+1)}}

此公式强调了JPX赛题的目标:根据时间点tt以及之前的历史数据,预测未来时间点t+2t+2的卖出价格与t+1t+1时买入价格的变化。

理解股票未来收益率的预测涉及许多复杂因素,包括公司基本面分析、宏观经济走势、市场趋势等。理论上对于股票预测的赛题来说,这些因素的综合分析将有助于更精确地预测未来收益率。

通过上面的介绍,相信小伙伴们应该了解了股票收益率Target的计算方法,不过值得注意的是

JPX赛题中股票收益率的计算代码不需要参赛者自己来计算,数据中已经以标签"Target"的形式提供。

1.2.2 投资组合与做多做空策略

投资组合是什么?

投资组合是由多种投资组成的集合,它通过将资金分散投资于不同的股票或其他资产,旨在分散风险、提高收益。在JPX赛题中,投资组合的构建和管理是赛题的关键环节。

JPX赛题要求参赛者在每次预测出2000支股票的预测收益率后,按照收益率大小进行排名。将排名最高的200支股票视为做多,将排名最低的200支股票视为做空。然后根据股票的排名进行加权,并假设股票在t+1交易日买入,t+2交易日卖出,计算出投资组合的总收益。

JPX赛题中的投资组合策略名词解释

  • 做多:预测收益率最高的200支股票,期望价格上涨。做多是购买资产或证券,期望价格上涨后卖出获利。
  • 做空:预测收益率最低的200支股票,期望价格下跌。做空是借入资产或证券卖出,期望价格下跌后买入归还获利。

什么是做多?什么是做空?

做多和做空是金融投资领域常用的术语,用于描述投资者在市场中对某个资产或证券的不同投资策略。

  1. 做多是指投资者购买某个资产或证券,期望其价格上涨,从中获取利润。当投资者认为某个资产的价格将上涨时,他们会买入该资产,以便在未来卖出时获得更高的价格。在JPX赛题中,将预测收益率最高的200支股票排名在最前面,是该赛题中的做多行为。

  2. 做空是指投资者借入某个资产或证券并立即卖出,期望其价格下跌,从中获取利润的投资策略。在做空的情况下,投资者预期所借入的资产将在未来的时间内价格下跌,然后再以更低的价格买入并归还给借出方,从而获得利润。同样的,在JPX赛题中,将预测收益率最低的200支股票排名在最后面,是该赛题中的做空行为。(除了股票,货币也可以做空)

1.3 理解比赛的评估指标——夏普比率

什么是夏普比率?

在投资组合管理中,衡量投资效果的不仅有收益,还有风险。夏普比率(Sharpe Ratio)是一种常用的评价指标,用于评估投资组合每单位风险所带来的超额收益。其计算公式为:

              SharpeRatio=RpRfσpSharpeRatio = \frac{R_p - R_f}{\sigma_p}

其中:

  • RpR_p表示投资组合的预期收益率
  • RfR_f​表示无风险利率,即可以稳定获得的最低收益率,通常以国债利率或银行存款利率作为代表
  • σpσ_p​表示投资组合的标准差,即投资组合收益的波动程度,反映了投资组合的风险水平

夏普比率的应用示例

假设有两个投资组合A和B,它们的预期收益和标准差如下表所示:

投资组合 预期收益 标准差
A 10% 15%
B 12% 20%

假设无风险利率为3%,那么A和B的夏普比率分别为:

              Sharpe RatioA=10%3%15%=0.467\text{Sharpe Ratio}_A = \frac{10\% - 3\%}{15\%} = 0.467

              Sharpe RatioB=12%3%20%=0.45\text{Sharpe Ratio}_B = \frac{12\% - 3\%}{20\%} = 0.45

可以看出,虽然B的预期收益高于A,但是B的标准差也高于A,说明B的风险也高于A。因此,A的夏普比率略高于B,说明A在单位风险上获得了更高的超额收益,投资效果更好。

如何理解夏普比率?

对于一个没有金融背景的人来说,可以用以下方法来理解夏普比率:

  1. 收益与风险的平衡:夏普比率是一种用来评价投资效果的指标,它不仅考虑了投资的收益,还考虑了投资的风险。一般来说,收益越高,风险也越高;收益越低,风险也越低。因此,不能单纯地用收益来判断投资是否优秀,还要看收益是以多大的风险为代价换来的。夏普比率通过超额收益和风险的比例来评价投资的整体效果。
  2. 超额收益的评价:夏普比率是一种相对指标,它用投资组合的预期收益减去无风险利率,得到超额收益。无风险利率是指不承担任何风险就可以获得的最低收益,例如把钱存入银行或者买国债。超额收益是指投资组合相对于无风险利率多赚了多少钱。夏普比率用超额收益除以投资组合的标准差,得到每单位风险所能带来的超额收益。每单位风险就是指承担一定程度的波动所面临的可能损失。因此,夏普比率反映了投资者为了获取更高的收益而多承担的风险是否值得。
  3. 投资组合的比较:夏普比率可以用来比较不同的投资组合或者基金,看哪一个在相同条件下表现更好。一般来说,夏普比率越高,说明投资效果越好;夏普比率越低,说明投资效果越差。如果夏普比率为负数,说明投资组合的预期收益低于无风险利率,即亏损了。

夏普比率是JPX赛题中的核心评价指标,它反映了投资组合的收益、风险和超额收益的平衡关系。理解和掌握夏普比率有助于更好地理解赛题目标和构建有效的模型。我们的目标是最大化夏普比率,从而实现投资效果的优化。

2. 认识JPX的比赛形式和比赛数据

2.1 认识code game

现在,小伙伴们应该对比赛的基本背景知识和评估指标有了一定的了解。下面,我们来深入探讨一下比赛的形式:比赛将以Code Game的形式展开。

  1. 什么是Code Game?
  2. Code Game和其他类型的比赛有什么区别?
  3. 什么样的比赛会使用Code Game?

我们先来了解kaggle的经典比赛类型:
  (1) Simple Competitions:这是 Kaggle 平台上最常见的比赛类型。在这类比赛中,参赛者会在比赛开始时获得全部的数据集,包括训练集和测试集。参赛者需要下载数据,在本地或 Kaggle Notebooks 中构建模型,然后生成预测文件并上传到 Kaggle 平台。例如我们之前提到的案例Home Credit Default Risk。这样做的优点是参赛者可以在本地进行模型训练和调试,只需要提交预测的csv文件,无需在线评估,可以快速获得得分结果;但是,其缺点在于参赛者可以查看测试集数据,并可能适当地利用测试集数据进行训练,存在数据泄露的风险。为了控制测试集数据泄露的问题并解决不同参赛者间的计算能力差异,kaggle引入了Two-stage Competitions和Code Competitions两种比赛方式。
  (2) Two-stage Competitions:这种比赛模式通过两个阶段来解决测试集泄露的问题。在第一阶段,参赛者必须在阶段结束时提交一个模型,这个模型能够对结果进行预测才有资格进入第二阶段,而且,在许多情况下,提交的模型是不允许修改的。在第二阶段开始时,会发布一个新的测试数据集,参赛者提交的模型需要对这个从未见过的测试数据集进行预测。这种比赛方式强调了模型的泛化能力。
  (3) Code Competitions:Code Competitions也叫Code game,是Kaggle比赛中的一种特殊类型。在这些比赛中,所有提交都必须从Kaggle Notebook内部进行,一般来说,这类比赛会有一些特定的技术限制和要求,例如CPU或GPU运行时间、是否可以使用外部数据以及是否可以访问互联网。这些限制旨在确保所有参与者在同样的条件下进行竞争,实现公平性。此类比赛有两个主要的特点:
    ①更加公平:因为所有的参赛者都在同一平台上进行模型训练和预测,这保证了所有参赛者享有相同的硬件资源。这样,获胜的模型通常比其他类型比赛中的模型简单得多,因为它们必须在平台施加的计算约束内运行;
    ②防止数据泄露:这种方式无需将比赛分为两个阶段,就可以有效防止测试集数据泄露。并且,由于是在线预测,比赛主办方可以随时更新测试集数据,提高比赛的动态性和挑战性。

  因此,使用 Code Game 形式的比赛通常包括需要实时预测的情况,或者涉及到有可能被参赛者操纵或滥用的数据。

  例如,股票和加密货币预测比赛可能会使用 Code Game 形式,因为保证比赛的公平性和防止数据滥用。并且在这些比赛中,数据是实时的并且会不断变化。如果允许参赛者在自己的环境中运行模型并提交预测结果,那么有可能会出现某些参赛者使用未来的数据(也就是在模型训练时还未知的数据)来优化他们的模型。通过使用 Code Game 形式,Kaggle 可以确保所有参赛者的模型都在相同的环境中运行,并且只使用到比赛规定的数据。

接下来我们一起来看一下JPX Tokyo Stock Exchange Prediction比赛的说明,体会一下什么叫做“数据是实时的并且会不断变化”:

首先,让我们来看一下官网上显示的比赛的数据集:

image

在这个数据集的一级目录下,我们可以看到包含五个文件夹和一个名为stock_list.csv的文件:

  1. data_specifications/:这个文件夹提供了每一列数据的详细定义,帮助我们理解每一列数据的具体含义和用途。
  2. example_test_files/:这个文件夹包含了公开的测试数据,主要是为了方便我们进行离线测试。这些数据和API传送的数据列是一样的,但并未包括Target列。我们可以通过Close列来计算Target列的值,它表示在第二天买入股票并在第三天卖出后的收益。此外,这个文件夹还提供了一个样本提交文件的示例,这将由API传送。
  3. jpx_tokyo_market_prediction/:这个文件夹包含启用API的文件。API将能在五分钟内传送所有的行,并且占用的内存将不超过0.5GB。(思考为什么官方要给这样一句说明)
  4. train_files/:这个文件夹包含了主要训练期间的数据。
  5. supplemental_files/:这个文件夹包含了动态窗口的补充训练数据。这些数据会在比赛主要阶段的5月初、6月初,以及提交阶段锁定前的一周进行更新。补充数据也会在预测阶段开始时进行一次更新,这样测试集的起始日就会是补充数据中最后一个交易日的次日。(下面会详细介绍)
  6. stock_list.csv:这个文件提供了证券代码与公司名称之间的映射,以及关于公司所在行业的基本信息。

在train_files、supplemental_files和example_test_files文件夹中,我们可以找到五个主要的数据文件:

  1. stock_prices.csv:这是我们主要关注的文件。它包括每只股票的每日收盘价和目标列(即我们需要预测的值)。
  2. options.csv:这个文件提供了基于更广泛市场的各种期权的状态数据。尽管期权本身并未直接参与得分的计算,但许多期权包含了对股票市场未来价格的预测,因此可能对我们的分析有所帮助。
  3. secondary_stock_prices.csv:虽然核心数据集只包含了2000种最常交易的股票,但在东京市场上还有许多流动性较低的证券也在交易。这个文件提供了这些证券的数据,虽然这些证券并未直接参与得分的计算,但它们可能有助于我们评估整个市场的状态。
  4. trades.csv:这个文件提供了上一周交易量的汇总信息。
  5. financials.csv:这个文件包含了季度收益报告的结果,可能对我们分析公司的财务状况有所帮助。
1
2
3
4
5
6
7
8
9
10
# df_stock_prices 为train_files目录下的核心股价数据(stock_prices.csv)
date_range_start = df_stock_prices['Date'].min()
date_range_end = df_stock_prices['Date'].max()
print(f"Train date range: {date_range_start} to {date_range_end}")
# df_stock_prices2 为supplemental_files目录下的核心股价数据(stock_prices.csv)
df_stock_prices2 = pd.read_csv('supplemental_files\stock_prices.csv')
date_range_start = df_stock_prices2['Date'].min()
date_range_end = df_stock_prices2['Date'].max()
print(f"Supplemental date range: {date_range_start} to {date_range_end}")
# 注意观察两个文件夹下股票数据的日期规律
Train date range: 2017-01-04 to 2021-12-03
Supplemental date range: 2021-12-06 to 2022-06-24

Supplemental补充文件的股价数据是接在train_files之后的

思考:为什么同样的股价数据,要被放在两个不同的文件夹里?

上官网看看赛程,或许能解开这个疑惑:

Description:比赛概况

当我们打开Kaggle比赛的页面,会首先看到比赛的概况和介绍。其中可能包括比赛的目标、背景、评估指标、奖励设置等基本信息。

image

点开Timeline,我们可以看到比赛的时间线

Timeline:比赛时间线与实时数据更新

比赛的时间线与官方提供的数据时间维度之间的关系非常重要。在JPX赛题中,有一些特殊的安排:

image

  我们需要留意的是,比赛的时间线和官方提供的数据时间维度之间存在一定的重合。实际上,在比赛期间,官方提供的数据中,名为supplemental_files的文件夹里的数据会实时更新,这种更新将持续到比赛提交结束前的一周。这意味着我们可以利用这些实时更新的数据来优化我们的模型并提升预测的准确度。

  与此同时,尽管比赛的提交截止日期定在7月5日,最终的结果却要等到10月份才会公布。这个延迟的原因在于比赛的目标是预测未来三个月股票交易市场的真实动态。因此,在提交截止日期之后,还需要等待三个月的时间,让真实的股票市场走势来验证模型的泛化能力和准确性。这个过程可以说是一次真刀真枪的测试,它不仅提高了比赛的挑战性,也增加了对模型在真实世界应用中表现的考察。

Evaluation

对于Code Game来说,在Evaluation里面除了会写评估指标(例如在这里是夏普比率),还会详细说明提交的格式、如何导入API环境、获取测试集、如何提交等。在下面baseline中还会详细讲到。

image

Code Requirements:代码要求

  1. 运行时间限制:GPU和CPU的使用时长不能超过9小时。
  2. 硬件和效率限制:部分比赛可能会有更严格的硬件限制,例如限制CPU个数和内存大小,禁止使用GPU等。

有些比赛还可能设置效率奖项,鼓励参赛者开发资源占用更低的模型。
这些要求旨在确保公平竞争,鼓励参赛者在追求模型性能的同时,也关注代码效率和资源优化。

image

2.2 认识比赛数据

和上一个案例一样,在这个阶段,我们将对数据进行更深入的探索。这个过程包括但不限于:

  • 数据规模:我们需要知道我们有多少数据,数据的维度是多少。

  • 数据类型:数据集中的每列数据是什么类型,例如数字、分类、时间等。

  • 数据分布:检查每个变量的分布,查看是否存在偏斜。对于偏斜严重的变量,我们可能需要进行适当的转换(如对数转换)来使其更接近正态分布。

  • 缺失值:数据集中是否存在缺失值,缺失值的数量是多少,它们分布在哪些列中。缺失值太多的变量包含的信息量太少,可能会对模型的预测能力产生负面影响。若是使用lightgbm建模的话,也可以考虑暂时不处理。

  • 异常值:主要指那些偏离正常范围太多的值,它们可能是录入错误或代表某种特定的业务场景。

  • 特征之间的关系:查看特征之间的相关性。如果两个特征高度相关,那么我们可能只需要保留其中一个。

  • 特征与目标变量的关系:查看每个特征与目标变量TARGET的关系。这可以帮助我们理解哪些特征可能对预测TARGET有用。

2.2.1 核心股价数据与业务理解

  1. 日期(Date):这是每条记录的日期。它是重要的,因为股票价格是随时间变化的,我们的目标是预测未来的价格,因此需要理解价格是如何随时间变化的。

  2. 证券代码(SecuritiesCode):这是每个股票的唯一标识符。每个公司都有一个唯一的证券代码,我们需要理解每个公司的价格走势。

  3. 开盘价(Open):开盘价是指股票在交易日开始时的第一笔交易价格。它代表了市场对该股票的初始估值,并且可能会受到前一交易日收盘后发生的各种因素(如公司新闻、经济数据发布等)的影响。

  4. 最高价(High):这是股票在交易日内的最高交易价格。最高价反映了市场在一天中对股票价值的最大估计。

  5. 最低价(Low):这是股票在交易日内的最低交易价格。最低价反映了市场在一天中对股票价值的最小估计。

  6. 收盘价(Close):这是股票在交易日结束时的价格。收盘价被认为是非常重要的,因为它反映了市场在交易日结束时对股票价值的估计。我们的目标是预测未来的收盘价。

  7. 成交量(Volume):这是在一天中交易的股票数量。大的交易量可能表示市场对股票的强烈兴趣,可能预示着价格的变动。(成交量和涨跌之间未必有很强的关联性,但成交量高表示市场当中分歧很大,可以参考下面的图片)

  8. 调整因子(AdjustmentFactor):这是 一个校正因子,用于调整股票价格,以反映股票分拆、股息、股权发行等活动的影响。这对于准确比较不同时间点的股票价格是非常重要的。

  9. 预期股息(ExpectedDividend):这是预期将要分配给股东的股息。股息可能会影响投资者对股票的需求,因此可能影响价格。

  10. 监管标志(SupervisionFlag):这是一个标志,表示股票是否处于监管状态。如果一只股票处于监管状态,可能会影响投资者的交易决策,因此可能影响价格。

  11. 目标(Target):这是我们要预测的目标变量,表示未来股票的收益率。

关于成交量和股价波动之间的关系,可以看下图:

这是某电气公司的8月2号这一天的交易情况,我们可以看到该股票涨停了,并且交易量很小,说明市场相对来说没有分歧,都一致看好这支股票。考虑到最近发生的事情,应该是受到“室温超导”新闻的影响,而这家公司的业务被认为和超导有关。

image

1
2
3
4
5
6
# 导入必要的库
import seaborn as sns
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from decimal import ROUND_HALF_UP, Decimal # 用于更精确的进行四舍五入
  • 数据规模
1
2
# 查看数据集的基本情况,数据集是按照日期进行排列的
df_stock_prices.head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target
0 20170104_1301 2017-01-04 1301 2734.0 2755.0 2730.0 2742.0 31400 1.0 NaN False 0.000730
1 20170104_1332 2017-01-04 1332 568.0 576.0 563.0 571.0 2798500 1.0 NaN False 0.012324
2 20170104_1333 2017-01-04 1333 3150.0 3210.0 3140.0 3210.0 270800 1.0 NaN False 0.006154
3 20170104_1376 2017-01-04 1376 1510.0 1550.0 1510.0 1550.0 11300 1.0 NaN False 0.011053
4 20170104_1377 2017-01-04 1377 3270.0 3350.0 3270.0 3330.0 150800 1.0 NaN False 0.003026
1
2
# 查看数据集的形状
df_stock_prices.shape
(2332531, 12)
  • 数据类型
1
2
3
# 我们发现日期是object类型,也就意味着我们后面需要进行转换
# 这也是为什么我们上面画图的时候需要进行转换
df_stock_prices.dtypes
RowId                object
Date                 object
SecuritiesCode        int64
Open                float64
High                float64
Low                 float64
Close               float64
Volume                int64
AdjustmentFactor    float64
ExpectedDividend    float64
SupervisionFlag        bool
Target              float64
dtype: object
  • 缺失值
1
2
3
4
5
6
7
8
def missing (df):
"""
计算每一列的缺失值及占比
"""
missing_number = df.isnull().sum().sort_values(ascending=False) # 每一列的缺失值求和后降序排序
missing_percent = (df.isnull().sum()/df.isnull().count()).sort_values(ascending=False) # 每一列缺失值占比
missing_values = pd.concat([missing_number, missing_percent], axis=1, keys=['Missing_Number', 'Missing_Percent']) # 合并为一个DataFrame
return missing_values
1
2
3
4
# "Open"、"High"、"Low"和"Close"字段各有7608个缺失值
# "ExpectedDividend"字段有2313666个缺失值
# "Target"字段有238个缺失值
missing(df_stock_prices)
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
Missing_Number Missing_Percent
ExpectedDividend 2313666 0.991912
Open 7608 0.003262
High 7608 0.003262
Low 7608 0.003262
Close 7608 0.003262
Target 238 0.000102
RowId 0 0.000000
Date 0 0.000000
SecuritiesCode 0 0.000000
Volume 0 0.000000
AdjustmentFactor 0 0.000000
SupervisionFlag 0 0.000000
"ExpectedDividend"字段的缺失值最多,几乎所有的记录都没有这个字段的数据,因为预期股息(ExpectedDividend)只在分红日才会有值,其他日期通常为NaN(即空值)。这是因为公司并不是每天都会发放股息,只有在公司决定分红,即发放利润给股东的那一天,预期股息才会有具体的数值。在这之后,直到下一次分红,预期股息都会是空值。
1
2
# 查看标签为缺失值的数据
df_stock_prices[df_stock_prices['Target'].isna()].sort_values(by='SecuritiesCode').head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target
1757328 20201002_2987 2020-10-02 2987 NaN NaN NaN NaN 0 1.0 NaN False NaN
283221 20170814_3540 2017-08-14 3540 NaN NaN NaN NaN 0 1.0 NaN False NaN
285099 20170815_3540 2017-08-15 3540 NaN NaN NaN NaN 0 1.0 NaN False NaN
286977 20170816_3540 2017-08-16 3540 NaN NaN NaN NaN 0 1.0 NaN False NaN
288855 20170817_3540 2017-08-17 3540 NaN NaN NaN NaN 0 1.0 NaN False NaN
1
2
# 只有这四支股票存在标签的缺失值
df_stock_prices[df_stock_prices['Target'].isna()].SecuritiesCode.unique()
array([3540, 4382, 4056, 2987], dtype=int64)
1
2
# 而且标签缺失的时候,股价也一定是缺失的,因为没有股价也就没有办法计算标签
df_stock_prices[(df_stock_prices['Target'].isna()) & (df_stock_prices['Close'].notna())]
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}

根据主办方的说明,这些缺失值是因为系统崩溃导致数据缺失,因此,后面我们需要根据业务对其进行填补。

  • 异常值

先来检查一下股票的数量是否能对得上

1
2
3
# 检查证券数量,确实是筛选了2000支市值靠前的股票
unique_securities = df_stock_prices['SecuritiesCode'].nunique()
print(f"数据集中有 {unique_securities} 种不同的证券。")
数据集中有 2000 种不同的证券。

关于nunique和unique的联系和区别:

都是用来处理数据框或序列中的唯一值

1
2
# .nunique() 函数返回数据框或序列中唯一值的数量
df_stock_prices['SecuritiesCode'].nunique()
1
2
# .unique() 函数返回数据框或序列中唯一值组成的数组
df_stock_prices['SecuritiesCode'].unique()
array([1301, 1332, 1333, ..., 4168, 7342, 4169], dtype=int64)
1
2
3
4
# 查看数据分布
# 设置dataframe的显示小数位,防止数字变成科学计数法,不便于我们进行观察
pd.set_option('display.float_format', lambda x: '%.2f' % x)
df_stock_prices.describe()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend Target
count 2332531.00 2324923.00 2324923.00 2324923.00 2324923.00 2332531.00 2332531.00 18865.00 2332293.00
mean 5894.84 2594.51 2626.54 2561.23 2594.02 691936.56 1.00 22.02 0.00
std 2404.16 3577.19 3619.36 3533.49 3576.54 3911255.94 0.07 29.88 0.02
min 1301.00 14.00 15.00 13.00 14.00 0.00 0.10 0.00 -0.58
25% 3891.00 1022.00 1035.00 1009.00 1022.00 30300.00 1.00 5.00 -0.01
50% 6238.00 1812.00 1834.00 1790.00 1811.00 107100.00 1.00 15.00 0.00
75% 7965.00 3030.00 3070.00 2995.00 3030.00 402100.00 1.00 30.00 0.01
max 9997.00 109950.00 110500.00 107200.00 109550.00 643654000.00 20.00 1070.00 1.12
可以看到收盘价的上四分位数和最大值之间差距比较大,为了更直观的观察,我们绘制收盘价的直方图
1
2
3
4
5
6
# 绘制收盘价的直方图
plt.figure(figsize=(10,6))
sns.histplot(data=df_stock_prices, x="Close", bins=50, kde=True)
# kde=True在直方图上方绘制核密度估计(KDE)曲线
plt.title('收盘价分布')
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_97_0.png)
从图中可以看出,大多数股票的收盘价都集中在一个较低的范围内,但也有一些股票的收盘价非常高。这可能表明股票的价格分布是偏态的,这是一个值得注意的点,因为它可能影响我们模型的性能。

1
2
# 为了查看最高收盘价的数据是否异常,我们查看最大收盘价对应的股票序号,并绘制这支股票的股价走势
df_stock_prices[df_stock_prices['Close'] == 109550]
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target
1952522 20210225_9983 2021-02-25 9983 108600.00 109550.00 107200.00 109550.00 621000 1.00 NaN False 0.03
1
2
3
4
5
6
7
8
9
10
11
12
13
# 选择自己想观察的特定股票
stock_9983 = df_stock_prices[df_stock_prices['SecuritiesCode'] == 9983]

# 将 Date 列转换为 datetime 格式
stock_9983['Date'] = pd.to_datetime(stock_9983['Date'])

# 绘制所选股票随时间变化的收盘价
plt.figure(figsize=(14,6))
plt.plot(stock_9983['Date'], stock_9983['Close'])
plt.title('股票 9983 收盘价随时间变化')
plt.xlabel('日期')
plt.ylabel('收盘价')
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_100_0.png)
我们通过观察股价的走势发现并无异常,并且通过stock_list.csv文件我们可以查到这只股票是零售行业,世界第三大休闲服公司:迅销公司

1
2
stock_list = pd.read_csv('stock_list.csv')
stock_list[stock_list['SecuritiesCode'] == 9983]
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
SecuritiesCode EffectiveDate Name Section/Products NewMarketSegment 33SectorCode 33SectorName 17SectorCode 17SectorName NewIndexSeriesSizeCode NewIndexSeriesSize TradeDate Close IssuedShares MarketCapitalization Universe0
4403 9983 20211230 FAST RETAILING CO.,LTD. First Section (Domestic) Prime Market 6100 Retail Trade 14 RETAIL TRADE 2 TOPIX Large70 20211230.00 65310.00 106073656.00 6927670473360.00 True
- 相关性分析

既然我们可以根据历史股价数据绘制价格走势图,那不妨我们来看看股价走势和AdjustmentFactor、ExpectedDividend以及SupervisionFlag之间的关系,与此同时,我会从业务层面解释这几个字段之间的逻辑关联。

AdjustmentFactor和股价之间的关系

1
2
3
# AdjustmentFactor为调整因子,查看调整因子的取值情况
# 如果一支股票AdjustmentFactor不为1,则说明公司进行了股价的调整
df_stock_prices['AdjustmentFactor'].value_counts()
1.00     2331801
0.50         328
5.00         113
10.00        103
0.33          59
2.00          36
0.25          33
0.20          20
0.91          10
0.83           6
0.77           6
0.67           4
0.10           3
0.95           3
0.80           2
0.14           1
0.87           1
4.00           1
20.00          1
Name: AdjustmentFactor, dtype: int64

思考:调整因子是什么意思?为什么会需要调整因子

股票价格通常被认为是一个公司市值的反映,正常情况下会随着市场供需、公司业绩、经济环境、投资者情绪、利率变动、政策变动等等发生变化,但是我们能看到的股票价格是每一股的价格(即股票单价),而市值=每股价格x股票总数量,有时候公司决策(股息发放、股票分拆/合并、配股、股票回购等)会影响市场上流动的股票总数,而这个时候公司的总市值并没有发生变化,我们能看到的股价就会发生剧烈的震荡,但实际上这种震荡并不能反应公司市值的变化,我们需要利用AdjustmentFactor进行股价调整,消除公司决策对股价的影响,从而得到能够真正反应公司真实价值变化的股价序列。

如果"AdjustmentFactor"大于1,那么意味着公司可能进行了股票分拆、配股或发放了股息等操作,这些操作会增加股票的数量,导致每股价格下降,因此我们需要通过调整因子来增大股价。

相反,如果"AdjustmentFactor"小于1,那么意味着公司可能进行了股票合并或股票回购等操作,这些操作会减少股票的数量,导致每股价格上升,因此我们需要通过调整因子来减小股价。

专业名词说明:

  1. 股息发放(Dividend distribution):公司将其利润的一部分以现金的形式分发给股东,这称为股息。当公司发放股息时,其股价通常会下降,因为公司的部分资产已经以股息的形式返回给了股东,公司的价值相应减少。所以,我们需要通过调整因子来修正这种股息发放对股价的影响。

  2. 股票分拆(Stock split):公司将每一股股票分成多股,比如一股分为两股。分拆后,股票的总数增加,每股价格下降,但公司的总市值不变。股票分拆后,我们需要对股价进行调整,以消除分拆对股价的影响。

  3. 股票合并(Stock merger):相反,公司可能会选择合并股票,比如五股合并为一股。合并后,股票的总数减少,每股价格上升,但公司的总市值不变。股票合并通常是公司股价较低时的策略,目的是通过提高股价来改善公司在市场上的形象。股票合并后,我们也需要对股价进行调整。

  4. 配股(Rights issue):公司发行新的股票给现有的股东,通常以低于市场价格的价格出售。这会增加公司的股票总数,可能会导致每股价格下降。配股可以帮助公司筹集资金,但也可能会稀释现有股东的股权,因此,我们需要对股价进行调整来反映这种变化。

  5. 股票回购(Stock buyback):公司购买市场上的自己的股票,减少股票的总数,这可能会导致每股价格上升。股票回购通常是公司利润丰厚时的策略,目的是通过减少流通在市场上的股票数量,提高每股收益,从而提升股价。股票回购后,我们需要对股价进行调整。

1
2
3
# 例如,我们来看一个股价调整的例子6861号股票
single_stock = df_stock_prices[df_stock_prices['SecuritiesCode'] == 6861]
single_stock[single_stock['AdjustmentFactor']!=1]
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target
16052 20170117_6861 2017-01-17 6861 83610.00 83610.00 82540.00 82600.00 146400 0.50 NaN False 0.01
1340160 20191118_6861 2019-11-18 6861 74020.00 74270.00 73550.00 74220.00 187300 0.50 NaN False 0.00
1
2
3
4
5
6
7
# 绘制收盘价的走势
plt.figure(figsize=(14, 7))
plt.plot(pd.to_datetime(single_stock['Date']), single_stock['Close'])
plt.title('股票 6861 收盘价随时间变化')
plt.xlabel('日期')
plt.ylabel('收盘价')
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_110_0.png)
可以看到这样的走势其实并不能反应真实的公司市值

因此,我们需要对原始的股价数据进行调整,这个函数名为adjust_price,主要用于调整原始股票价格数据。输入的参数是包含股票价格的DataFrame,返回的是包含调整后的收盘价AdjustedClose的DataFrame。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def adjust_price(price):
"""
调整股票价格数据。

参数:
price (pd.DataFrame) : 包含股票价格信息的 pd.DataFrame。该 DataFrame 应至少包含以下列:
- Date (str) : 日期,格式为 "%Y-%m-%d"。
- AdjustmentFactor (float) : 调整因子。
- Close (float) : 收盘价。

返回:
pd.DataFrame: 含有新生成的 AdjustedClose 的股票价格 DataFrame。

函数功能:
该函数将输入的原始股价数据进行处理,生成带有调整后收盘价(AdjustedClose)的新 DataFrame。
AdjustedClose 的计算方式为原收盘价与调整因子(AdjustmentFactor)的累计乘积,结果保留一位小数。
若计算后的 AdjustedClose 为 0,则替换为空值(np.nan),并对此列数据进行向前填充,以保持数据完整性。
"""

# 将 Date 列转换为 datetime 格式
# 因为我们在前面查看数据类型的时候发现Date为object类型
price.loc[: ,"Date"] = pd.to_datetime(price.loc[: ,"Date"], format="%Y-%m-%d")

def generate_adjusted_close(df):
"""
参数:
df (pd.DataFrame) : 单个 SecuritiesCode 的股票价格
返回:
df (pd.DataFrame): 单个 SecuritiesCode 的带有 AdjustedClose 的股票价格
"""
# 排序数据以生成 CumulativeAdjustmentFactor,因为cumprod是从上往下累乘
# 所以需要保证日期是降序的,dataframe上面是过去的日期
df = df.sort_values("Date", ascending=False)

# 生成 CumulativeAdjustmentFactor
df.loc[:, "CumulativeAdjustmentFactor"] = df["AdjustmentFactor"].cumprod()# 累乘,可以看下面的代码示例

# 根据官方对AdjustmentFactor的说明,生成 AdjustedClose
df.loc[:, "AdjustedClose"] = (
df["CumulativeAdjustmentFactor"] * df["Close"]
).map(lambda x: float( # map表示对所有元素进行映射操作
Decimal(str(x)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP)
)) # 使用Decimal进行四舍五入,可以看下面的示例代码

# 反转顺序,恢复数据的升序排列顺序
df = df.sort_values("Date")

# 填充 AdjustedClose,将 0 替换为 np.nan
df.loc[df["AdjustedClose"] == 0, "AdjustedClose"] = np.nan

# 向前填充 AdjustedClose
df.loc[:, "AdjustedClose"] = df.loc[:, "AdjustedClose"].ffill()
return df

# 生成 AdjustedClose
price = price.sort_values(["SecuritiesCode", "Date"])
price = price.groupby("SecuritiesCode").apply(generate_adjusted_close).reset_index(drop=True)
return price

cumprod累乘示例代码

1
2
3
Ser = pd.Series([1, 2, 3, 4])
# 计算累积积
Ser.cumprod()
0     1
1     2
2     6
3    24
dtype: int64

使用Decimal进行四舍五入示例代码

1
2
3
4
x = 3.14159 
# 0.1表示小数点后一位
result = Decimal(x).quantize(Decimal('0.1')) # 舍入模式rounding默认为ROUND_HALF_EVEN
result
Decimal('3.1')

ROUND_HALF_EVEN被称为银行家舍入法。它的规则是:如果要舍入的数字恰好在两个整数之间,那么就将其舍入到最近的偶数。例如,3.5会被舍入为4,而2.5会被舍入为2。

这种舍入方式的优点在于它能够减少累积误差。当我们对一组数据进行多次四舍五入时,如果总是采用普通的四舍五入方式(即ROUND_HALF_UP),那么最终结果可能会有一定的偏差。而使用ROUND_HALF_EVEN则能够有效地减少这种偏差,使得最终结果更加准确。

1
Decimal(2.5).quantize(Decimal('1'))
Decimal('2')
1
Decimal(3.5).quantize(Decimal('1'))
Decimal('4')
1
2
# 如果我们想使用常规的四舍五入,要设置rounding=ROUND_HALF_UP
Decimal(2.5).quantize(Decimal('1'),rounding=ROUND_HALF_UP)
Decimal('3')

最后,如果想要进行更精确的四舍五入,应该使用字符串形式的数字作为输入,而不是直接使用浮点数。这是因为浮点数本身就不是精确的,它们可能会在计算机存储和运算过程中产生一些误差。即:

1
Decimal(2.675).quantize(Decimal('0.01'),rounding=ROUND_HALF_UP)
Decimal('2.67')
1
Decimal(str(2.675)).quantize(Decimal('0.01'),rounding=ROUND_HALF_UP)
Decimal('2.68')

根据官方的说明,我们使用普通的四舍五入ROUND_HALF_UP即可

扩展】为什么不使用round而要使用decimal类型进行四舍五入?

round()函数是一个内置函数,它可以对浮点数进行四舍五入。例如,如果我们想要将3.14159保留到小数点后一位,我们可以这样写:round(3.14159, 1),结果为3.1。

然而,由于浮点数的存储方式,round()函数可能会产生一些意想不到的结果。例如,round(2.675, 2)的结果是2.67,而不是2.68。这是因为2.675实际上被存储为一个接近但不等于2.675的值。

为了避免这种情况,我们可以使用Decimal类来进行精确的十进制计算。Decimal类提供了更多的控制和精度,并且可以避免浮点数运算中的一些问题。

1
round(2.675, 2)
2.67

调用上面定义的adjust_price函数

1
2
df_stock_prices = adjust_price(df_stock_prices)
df_stock_prices.head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target CumulativeAdjustmentFactor AdjustedClose
0 20170104_1301 2017-01-04 1301 2734.00 2755.00 2730.00 2742.00 31400 1.00 NaN False 0.00 1.00 2742.00
1 20170105_1301 2017-01-05 1301 2743.00 2747.00 2735.00 2738.00 17900 1.00 NaN False 0.00 1.00 2738.00
2 20170106_1301 2017-01-06 1301 2734.00 2744.00 2720.00 2740.00 19900 1.00 NaN False -0.00 1.00 2740.00
3 20170110_1301 2017-01-10 1301 2745.00 2754.00 2735.00 2748.00 24200 1.00 NaN False -0.01 1.00 2748.00
4 20170111_1301 2017-01-11 1301 2748.00 2752.00 2737.00 2745.00 9300 1.00 NaN False -0.00 1.00 2745.00
1
2
3
4
5
6
7
8
single_stock = df_stock_prices[df_stock_prices['SecuritiesCode'] == 6861]
# 重新绘制调整之后收盘价的走势
plt.figure(figsize=(14, 7))
plt.plot(pd.to_datetime(single_stock['Date']), single_stock['AdjustedClose'])
plt.title('股票 6861 调整后收盘价随时间变化')
plt.xlabel('日期')
plt.ylabel('调整之后的收盘价')
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_129_0.png)

SupervisionFlag和调整之后股价的关系

1
2
# 查看SupervisionFlag为True的股票数据
df_stock_prices[df_stock_prices['SupervisionFlag']==1].SecuritiesCode.unique()
array([2729, 3001, 3540, 3919, 4526, 4699, 5008, 5486, 6067, 6465, 6470,
       6502, 6637, 7868, 8806], dtype=int64)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 筛选出股票代码为2729的数据
single_stock_2729 = df_stock_prices[df_stock_prices['SecuritiesCode'] == 2729]
# 选取被监管的数据
single_stock_2729_supervised = single_stock_2729[single_stock_2729['SupervisionFlag'] == True]

# 绘制股票2729的收盘价随时间变化的走势
plt.figure(figsize=(14, 7))
# 绘制股价走势
plt.plot(pd.to_datetime(single_stock_2729['Date']), single_stock_2729['AdjustedClose'], label='收盘价')
# 绘制被监管时的点,以便于更直观的看到何时被监管
plt.scatter(pd.to_datetime(single_stock_2729_supervised['Date']), single_stock_2729_supervised['AdjustedClose'], c='red', label='被监管')
plt.title('股票 2729 在受监管时调整之后收盘价随时间变化')
plt.xlabel('日期')
plt.ylabel('调整之后的收盘价')
plt.legend()
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_132_0.png)

接下来我们来看一下受到监管的股票股价都是如何波动的

1
2
3
4
5
6
7
8
9
10
11
for i in [3001, 3540, 3919, 4526, 4699, 5008, 5486, 6067, 6465, 6470,6502, 6637, 7868, 8806]:    
single_stock_2729 = df_stock_prices[df_stock_prices['SecuritiesCode'] == i]
single_stock_2729_supervised = single_stock_2729[single_stock_2729['SupervisionFlag'] == True]
plt.figure(figsize=(14, 7))
plt.plot(pd.to_datetime(single_stock_2729['Date']), single_stock_2729['AdjustedClose'], label='收盘价')
plt.scatter(pd.to_datetime(single_stock_2729_supervised['Date']), single_stock_2729_supervised['AdjustedClose'], c='red', label='被监管')
plt.title(f'股票 {i} 在受监管时调整之后收盘价随时间变化')
plt.xlabel('日期')
plt.ylabel('调整之后的收盘价')
plt.legend()
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_134_0.png)

![png](JPX Tokyo Stock Exchange Prediction/output_134_1.png)

![png](JPX Tokyo Stock Exchange Prediction/output_134_2.png)

![png](JPX Tokyo Stock Exchange Prediction/output_134_3.png)

![png](JPX Tokyo Stock Exchange Prediction/output_134_4.png)
![png](JPX Tokyo Stock Exchange Prediction/output_134_7.png)
![png](JPX Tokyo Stock Exchange Prediction/output_134_8.png)
![png](JPX Tokyo Stock Exchange Prediction/output_134_9.png)
![png](JPX Tokyo Stock Exchange Prediction/output_134_10.png)
![png](JPX Tokyo Stock Exchange Prediction/output_134_11.png)
![png](JPX Tokyo Stock Exchange Prediction/output_134_12.png)
![png](JPX Tokyo Stock Exchange Prediction/output_134_13.png)

通过以上的观察,我们发现受监管并不一定会导致股价的剧烈波动,这是因为监管可能涉及许多不同的方面,例如:

  1. 财务报告和透明度:例如,公司可能因为财务造假、误导性财务报告、未能及时披露重要信息等行为受到监管。

  2. 公司治理:这可能涉及到公司的决策过程、董事会的组成和行为、股东权益等。例如,公司可能因为未能妥善处理利益冲突、未能保护股东权益等问题受到监管。

  3. 合规问题:公司可能因为违反特定的法律法规,如证券法、反垄断法、环保法规等受到监管。

  4. 风险管理:如果公司未能妥善管理其业务风险,如信贷风险、市场风险、流动性风险等,可能会受到监管。

  5. 不当行为:包括欺诈、内幕交易、操纵市场等非法行为。

  6. 市场操作:如股票回购、股票分拆、股票合并、发放股息等,这些操作可能需要得到监管机构的批准。

也就是说,是否被监管本身和股价之间的波动未必有必然的联系,如果我们想进一步探索被监管和股价波动之间的联系,需要知道具体的监管原因,监管的时间,监管的严重性等。例如,如果一家公司因为财务造假被监管,这可能会引发市场的不信任,导致股价下跌。另一方面,如果监管只是关于公司操作流程的一些小改动,可能不会对股价产生太大影响。

ExpectedDividend和调整之后股价的关系

1
2
# 查看ExpectedDividend预期股息的取值
df_stock_prices['ExpectedDividend'].value_counts()
0.00      3551
10.00      901
20.00      838
15.00      797
25.00      789
          ... 
236.00       1
205.00       1
16.80        1
298.00       1
5.17         1
Name: ExpectedDividend, Length: 446, dtype: int64
1
2
3
4
5
6
7
8
9
# 画核密度估计图
sns.kdeplot(data=df_stock_prices['ExpectedDividend'], fill=True)

# 添加标题和标签
plt.title('预期股息的核密度估计')
plt.xlabel('预期股息')
plt.ylabel('密度')
# 显示图形
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_138_0.png)

通过核密度估计图我们发现大部分预期股息都在0-100之间,但是仍然有少部分股票的预期股息很高,一般来说,高预期股息的股票的平均价格要高于中等和低预期股息的股票:

1
2
3
4
# 分组分析
# 使用qcut按照股息将数据划分为三个组'Low', 'Medium', 'High'
df_stock_prices['Dividend_Group'] = pd.qcut(df_stock_prices['ExpectedDividend'], 3, labels=['Low', 'Medium', 'High'])
df_stock_prices.head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target Dividend_Group
0 20170104_1301 2017-01-04 1301 2734.00 2755.00 2730.00 2742.00 31400 1.00 NaN False 0.00 NaN
1 20170104_1332 2017-01-04 1332 568.00 576.00 563.00 571.00 2798500 1.00 NaN False 0.01 NaN
2 20170104_1333 2017-01-04 1333 3150.00 3210.00 3140.00 3210.00 270800 1.00 NaN False 0.01 NaN
3 20170104_1376 2017-01-04 1376 1510.00 1550.00 1510.00 1550.00 11300 1.00 NaN False 0.01 NaN
4 20170104_1377 2017-01-04 1377 3270.00 3350.00 3270.00 3330.00 150800 1.00 NaN False 0.00 NaN
可以看到前五行全是空,这是因为只有发放了股息才会被分组,而那些没有发放股息的日子都会为空
1
2
# 我们可以通过取出Dividend_Group不为空的部分来查看是否划分完成
df_stock_prices[df_stock_prices['Dividend_Group'].notna()].head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target Dividend_Group
13269 20170116_2590 2017-01-16 2590 6060.00 6130.00 6060.00 6060.00 125800 1.00 30.00 False 0.01 High
13726 20170116_4699 2017-01-16 4699 1119.00 1119.00 1081.00 1087.00 11500 1.00 0.00 False -0.03 Low
26179 20170125_1928 2017-01-25 1928 1886.00 1894.50 1865.00 1871.50 3310500 1.00 32.00 False -0.02 High
26249 20170125_2217 2017-01-25 2217 515.00 517.00 512.00 512.00 162000 1.00 4.00 False 0.02 Low
26281 20170125_2353 2017-01-25 2353 153.00 154.00 151.00 152.00 431700 1.00 0.00 False -0.01 Low
1
2
3
# 可以看到不同股息等级的平均股价差别确实很大
# 高股息的股价确实高很多
df_stock_prices.groupby('Dividend_Group')['AdjustedClose'].mean()
Dividend_Group
Low      1776.10
Medium   1793.46
High     3931.23
Name: AdjustedClose, dtype: float64
1
2
# 而且每一个分组的股票数量并不少
df_stock_prices['Dividend_Group'].value_counts()
Low       6376
Medium    6341
High      6148
Name: Dividend_Group, dtype: int64
1
2
# 上面三类加起来也不过1.2万条数据,而数据集一共有200多万条数据
df_stock_prices.shape
(2332531, 15)

正如我们前面看到的那样,大部分ExpectedDividend字段为空值,但是投资者可以根据预期股息金额来估计股息收益,这是投资决策的一个重要因素。投资者可能会更倾向于投资那些预期能够提供稳定且较高股息收益的公司。

传统的金融理论认为股息金额和股价之间存在一定的联系。一方面,如果一家公司能够持续并稳定地支付高额的股息,这可能表明该公司的运营状况良好,有稳定的现金流,这对于投资者来说是吸引人的,可能会推高其股价。另一方面,根据股利折现模型(Dividend Discount Model,DDM),股票的理论价值等于其未来股息现值的总和,因此股息金额的变动可能会影响股票的理论价值,进而影响股价。但实际情况可能会更复杂,股价可能会受到许多其他因素的影响,如市场情绪、经济环境、公司的未来预期等:

1
df_stock_prices.groupby('Dividend_Group')['Target'].mean()
Dividend_Group
Low      -0.00
Medium   -0.01
High     -0.02
Name: Target, dtype: float64

我们可以通过绘制不同股息等级(Dividend_Group)的股票的均价走势来探索这种关系。

具体实现上,我们应该计算每一个Dividend_Group取值情况下【不同日期】的【所有股票收盘价均值】。然而,大部分Dividend_Group都是空值,因此我们需要进行填充,考虑到这里的特殊情况,这里我们可以使用向前填充向后填充
以下是示例代码:

1
2
# 例如我们以1301股票为例,大部分Dividend_Group都是缺失值
df_stock_prices[['SecuritiesCode','Date','Dividend_Group']].head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
SecuritiesCode Date Dividend_Group
0 1301 2017-01-04 NaN
1 1301 2017-01-05 NaN
2 1301 2017-01-06 NaN
3 1301 2017-01-10 NaN
4 1301 2017-01-11 NaN
1
2
# 可以看到1301股票的最早一笔股息是在2017年的3月17日
df_stock_prices[(df_stock_prices['SecuritiesCode']==1301) & (df_stock_prices['Dividend_Group'].notna())]
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target CumulativeAdjustmentFactor AdjustedClose Dividend_Group
56 20170327_1301 2017-03-27 1301 3125.00 3135.00 3110.00 3120.00 89300 1.00 60.00 False -0.03 1.00 3120.00 High
301 20180326_1301 2018-03-26 1301 3785.00 3860.00 3780.00 3860.00 43900 1.00 60.00 False -0.03 1.00 3860.00 High
545 20190325_1301 2019-03-25 1301 3015.00 3040.00 2995.00 3040.00 51300 1.00 60.00 False -0.02 1.00 3040.00 High
787 20200326_1301 2020-03-26 1301 2650.00 2714.00 2601.00 2689.00 65000 1.00 70.00 False -0.05 1.00 2689.00 High
1032 20210326_1301 2021-03-26 1301 3250.00 3285.00 3245.00 3255.00 48700 1.00 70.00 False -0.04 1.00 3255.00 High
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 为了展示什么叫做向前填充和向后填充,我们选取1301这只股票在2017年分红日期相邻的5条数据
# 用于理解向前填充和向后填充
df = df_stock_prices[df_stock_prices['SecuritiesCode']==1301][['Date','Dividend_Group']][55:60]

print("原始数据:")
print(df)

# 使用向前填充
df_ffill = df.ffill() # forward fill
print("\n向前填充:")
print(df_ffill)

# 使用向后填充
df_bfill = df.bfill() # backward fill
print("\n向后填充:")
print(df_bfill)
原始数据:
         Date Dividend_Group
55 2017-03-24            NaN
56 2017-03-27           High
57 2017-03-28            NaN
58 2017-03-29            NaN
59 2017-03-30            NaN

向前填充:
         Date Dividend_Group
55 2017-03-24            NaN
56 2017-03-27           High
57 2017-03-28           High
58 2017-03-29           High
59 2017-03-30           High

向后填充:
         Date Dividend_Group
55 2017-03-24           High
56 2017-03-27           High
57 2017-03-28            NaN
58 2017-03-29            NaN
59 2017-03-30            NaN

向前填充是用非空前值填充缺失的观测值,也就是用上一个有效的数据来替代缺失值。

向后填充是用非空后值填充缺失的观测值,也就是用下一个有效的数据来替代缺失值。

通过向前填充和向后填充,我们可以使得相同股票在相邻时间点的股息等级保持一致。

1
2
3
4
# 使用向后填充
df_bfill = df.ffill().bfill()
print("\n向前后填充:")
df_bfill
向前后填充:


.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
Date Dividend_Group
55 2017-03-24 High
56 2017-03-27 High
57 2017-03-28 High
58 2017-03-29 High
59 2017-03-30 High
1
2
3
4
5
6
7
# 绘制不同股息等级的股票均价走势

# 再根据 SecuritiesCode 向前后填充 Dividend_Group 的空值
df_stock_prices['Dividend_Group'] = df_stock_prices.groupby('SecuritiesCode')['Dividend_Group'].ffill().bfill()

# 查看填充结果
df_stock_prices['Dividend_Group'].value_counts()
Medium    788248
Low       780965
High      763318
Name: Dividend_Group, dtype: int64

向前填充和向后填充:一般来说,我们会根据数据的特性和我们的需求来选择使用哪种填充方法。例如,如果我们正在处理时间序列数据,并且我们认为过去的数据更有可能影响当前的数据,那么我们可能会选择使用向前填充。相反,如果我们认为未来的数据更有可能影响当前的数据,那么我们可能会选择使用向后填充。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
plt.figure(figsize=(12, 8))

# 对每个股息组绘制一条曲线
for group in ['Low', 'Medium', 'High']:
# 筛选出属于当前股息组的数据
data = df_stock_prices[df_stock_prices['Dividend_Group'] == group]
# 按日期分组,计算每个日期的平均 AdjustedClose 值
data_group_by_date = data.groupby('Date')['AdjustedClose'].mean()
# 绘制曲线
plt.plot(data_group_by_date.index, data_group_by_date.values, label=group)

# 显示图例
plt.legend()
# 设置 x 轴标签
plt.xlabel('日期')
# 设置 y 轴标签
plt.ylabel('平均 AdjustedClose')
# 设置标题
plt.title('按股息组划分的平均 AdjustedClose 随时间变化')
# 显示图形
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_157_0.png)
从图中我们可以看到,正如我们上面说的:股价可能会受到许多其他因素的影响,如市场情绪、经济环境、公司的未来预期等,虽然股息高的股票的平均收盘价也高,但是所有组别的股票价格随时间的变化趋势却非常一致。

接下来我们使用皮尔逊相关系数和热力图来看特征之间的相关性

1
2
3
# 计算特征之间的相关性
correlation = df_stock_prices.corr()
correlation
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target CumulativeAdjustmentFactor AdjustedClose
SecuritiesCode 1.00 0.02 0.02 0.02 0.02 0.04 0.00 0.03 -0.00 -0.00 0.02 0.03
Open 0.02 1.00 1.00 1.00 1.00 -0.04 -0.01 0.56 -0.00 -0.00 -0.11 0.93
High 0.02 1.00 1.00 1.00 1.00 -0.04 -0.01 0.56 -0.00 -0.00 -0.11 0.93
Low 0.02 1.00 1.00 1.00 1.00 -0.04 -0.01 0.56 -0.00 -0.00 -0.11 0.93
Close 0.02 1.00 1.00 1.00 1.00 -0.04 -0.01 0.56 -0.00 -0.00 -0.11 0.93
Volume 0.04 -0.04 -0.04 -0.04 -0.04 1.00 0.01 -0.02 0.10 -0.00 0.19 -0.02
AdjustmentFactor 0.00 -0.01 -0.01 -0.01 -0.01 0.01 1.00 NaN -0.00 -0.00 0.06 0.00
ExpectedDividend 0.03 0.56 0.56 0.56 0.56 -0.02 NaN 1.00 -0.01 -0.15 -0.10 0.54
SupervisionFlag -0.00 -0.00 -0.00 -0.00 -0.00 0.10 -0.00 -0.01 1.00 0.00 0.03 -0.00
Target -0.00 -0.00 -0.00 -0.00 -0.00 -0.00 -0.00 -0.15 0.00 1.00 -0.00 -0.01
CumulativeAdjustmentFactor 0.02 -0.11 -0.11 -0.11 -0.11 0.19 0.06 -0.10 0.03 -0.00 1.00 0.04
AdjustedClose 0.03 0.93 0.93 0.93 0.93 -0.02 0.00 0.54 -0.00 -0.01 0.04 1.00
1
2
3
4
5
6
7
8
plt.figure(figsize=(12,10))
# mask 参数用于只显示热力图的一半
mask = np.triu(np.ones_like(correlation, dtype=bool))
# 使用 seaborn 的 heatmap 函数来生成热力图
sns.heatmap(correlation, annot=True, cmap='coolwarm', mask=mask)

# 显示图形
plt.show()

![png](JPX Tokyo Stock Exchange Prediction/output_161_0.png)
在我们的热力图中,我们可以观察到以下关键点:

  1. ‘Open’, ‘High’, ‘Low’, 'Close’四个特征的相关性接近1,表明这四个特征之间存在非常强的正相关关系。这是预期中的,因为这四个特征都是描述股票在一个交易日内的价格变化,他们之间的值通常会非常接近,所以相关性很高。然而,在量化交易领域,特征间高度相关(强共线性)是一个严重的问题。强共线性可能导致模型过于依赖某些特征,从而产生过拟合。过拟合可能导致模型在训练数据上表现良好,但在测试数据上表现糟糕,这对量化交易公司来说可能导致巨大的损失。因此,在构建模型时,我们需要谨慎处理高度相关的特征,例如,我们可能只选择一组高度相关特征中的一个特征进行建模。

  2. ‘Target’ 和 ‘Volume’ 之间的相关性相对较低,这可能说明单日回报率与交易量之间没有直接的、强烈的关系。这可以引导我们进一步研究这两个特征之间的具体关系,以及它们如何与其他特征交互影响股价。

  3. 我们还可以注意到,预期股息’ExpectedDividend’与股价之间存在较强的关联性,而且’ExpectedDividend’与目标变量’Target’之间的相关性相对于所有特征来说是最强的。这与我们之前基于业务理解的判断一致,即预期股息可能对股票的未来收益有影响。这提醒我们,有时候,直觉和业务理解同样重要,可以引导我们在复杂的数据分析过程中找到重要的线索。

2.2.2 其他四张表格与业务理解

次级市场交易数据secondary_stock_prices.csv

这里的次级市场交易数据只是没有入选股票池的股票信息,同样能反映股票的交易价格、交易量等信息。

1
2
3
# 次级市场交易数据
secondary_stock_prices = pd.read_csv('train_files\secondary_stock_prices.csv')
secondary_stock_prices.head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
RowId Date SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag Target
0 20170104_1305 2017-01-04 1305 1594.00 1618.00 1594.00 1615.00 538190 1.00 NaN False -0.00
1 20170104_1306 2017-01-04 1306 1575.00 1595.00 1573.00 1593.00 2494980 1.00 NaN False -0.00
2 20170104_1308 2017-01-04 1308 1557.00 1580.00 1557.00 1578.00 526100 1.00 NaN False -0.00
3 20170104_1309 2017-01-04 1309 28810.00 29000.00 28520.00 28780.00 403 1.00 NaN False 0.01
4 20170104_1311 2017-01-04 1311 717.00 735.00 717.00 734.00 5470 1.00 NaN False 0.00
可以看到它和核心股价数据特征是一样的,只是股票代码取值会不一样。次级市场数据能够为我们提供更多股票价格的动态变化,有助于分析股票的趋势和波动。

股票列表数据stock_list.csv

这张数据集里主要是涵盖了股票的基本信息、行业分类、交易信息等方面。

1
2
stock_list = pd.read_csv('stock_list.csv')
stock_list.head(2)
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
SecuritiesCode EffectiveDate Name Section/Products NewMarketSegment 33SectorCode 33SectorName 17SectorCode 17SectorName NewIndexSeriesSizeCode NewIndexSeriesSize TradeDate Close IssuedShares MarketCapitalization Universe0
0 1301 20211230 KYOKUYO CO.,LTD. First Section (Domestic) Prime Market 50 Fishery, Agriculture and Forestry 1 FOODS 7 TOPIX Small 2 20211230.00 3080.00 10928283.00 33659111640.00 True
1 1305 20211230 Daiwa ETF-TOPIX ETFs/ ETNs NaN - - - - - - 20211230.00 2097.00 3634635625.00 7621830905625.00 False
1
2
# 更多行业信息,请查看附录的字段解释,重点看SectorName部分的解释
stock_list['33SectorName'].value_counts()
Information & Communication                      551
Services                                         521
-                                                500
Retail Trade                                     359
Wholesale Trade                                  320
Electric Appliances                              255
Machinery                                        230
Chemicals                                        218
Construction                                     170
Real Estate                                      143
Foods                                            125
Other Products                                   116
Transportation Equipment                          95
Banks                                             93
Metal Products                                    90
Pharmaceutical                                    77
Land Transportation                               62
Glass and Ceramics Products                       56
Textiles and Apparels                             52
Precision Instruments                             50
Securities and Commodities Futures                44
Iron and Steel                                    44
Warehousing and Harbor Transportation Service     39
Other Financing Business                          38
Nonferrous Metals                                 36
Electric Power and Gas                            25
Pulp and Paper                                    24
Rubber Products                                   19
Insurance                                         15
Oil and Coal Products                             13
Marine Transportation                             13
Fishery, Agriculture and Forestry                 12
Mining                                             7
Air Transportation                                 5
Name: 33SectorName, dtype: int64

这些数据有助于我们理解股票所属的行业、市场地位等关键信息,这些信息是分析股票走势的基础。

期权数据options

期权数据中包含了期权相关的数据,期权是一种金融衍生品,给予买方在未来某一时间以特定价格购买或出售资产的权利。期权数据中包含了期权相关的数据,如期权类型、执行价格、到期日期等。

1
2
3
# 期权数据
options = pd.read_csv('train_files\options.csv')
options.head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
DateCode Date OptionsCode WholeDayOpen WholeDayHigh WholeDayLow WholeDayClose NightSessionOpen NightSessionHigh NightSessionLow ... Putcall LastTradingDay SpecialQuotationDay SettlementPrice TheoreticalPrice BaseVolatility ImpliedVolatility InterestRate DividendRate Dividend
0 20170104_132010018 2017-01-04 132010018 650.00 650.00 480.00 480.00 0.0000 0.0000 0.0000 ... 1 20170112 20170113 480.00 478.46 17.47 17.59 0.01 0.00 0.00
1 20170104_132010118 2017-01-04 132010118 0.00 0.00 0.00 0.00 0.0000 0.0000 0.0000 ... 1 20170112 20170113 575.00 571.14 17.47 16.50 0.01 0.00 0.00
2 20170104_132010218 2017-01-04 132010218 0.00 0.00 0.00 0.00 0.0000 0.0000 0.0000 ... 1 20170112 20170113 680.00 677.37 17.47 15.86 0.01 0.00 0.00
3 20170104_132010318 2017-01-04 132010318 0.00 0.00 0.00 0.00 0.0000 0.0000 0.0000 ... 1 20170112 20170113 795.00 791.04 17.47 15.23 0.01 0.00 0.00
4 20170104_132010518 2017-01-04 132010518 0.00 0.00 0.00 0.00 0.0000 0.0000 0.0000 ... 1 20170112 20170113 910.00 909.99 17.47 14.59 0.01 0.00 0.00

5 rows × 31 columns

通过分析期权数据,我们可以了解市场对未来股价方向的预期,有助于更准确地预测股票未来的价格。

上周交易量trades.csv

这个数据集主要提供了不同类型的交易者在一个交易周内的交易数据,包括销售额、购买额、交易总额和平衡(销售额和购买额的差额)。通过这个数据集,你可以了解不同类型的交易者在市场上的行为,这可能会对预测股票价格有所帮助。

1
2
3
# 上周交易量结果,可以看到大部分都是缺失值
trades = pd.read_csv('train_files\\trades.csv')
trades[trades.notna()].head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
Date StartDate EndDate Section TotalSales TotalPurchases TotalTotal TotalBalance ProprietarySales ProprietaryPurchases ... CityBKsRegionalBKsEtcTotal CityBKsRegionalBKsEtcBalance TrustBanksSales TrustBanksPurchases TrustBanksTotal TrustBanksBalance OtherFinancialInstitutionsSales OtherFinancialInstitutionsPurchases OtherFinancialInstitutionsTotal OtherFinancialInstitutionsBalance
0 2017-01-04 NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 2017-01-05 NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 2017-01-06 NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 2017-01-10 NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 2017-01-11 NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 56 columns

1
trades[trades.notna()].shape
(1712, 56)

但是我们可以看到trades真正有用的数据并不多,对我们的预测或许起不到一个很好的帮助作用

季度收益报告结果financials.csv

季度收益报告展示了公司的财务状况,如收入、利润、债务等。

1
2
3
# 季度收益报告结果
financials = pd.read_csv('train_files\\financials.csv')
financials.head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
DisclosureNumber DateCode Date SecuritiesCode DisclosedDate DisclosedTime DisclosedUnixTime TypeOfDocument CurrentPeriodEndDate TypeOfCurrentPeriod ... ForecastEarningsPerShare ApplyingOfSpecificAccountingOfTheQuarterlyFinancialStatements MaterialChangesInSubsidiaries ChangesBasedOnRevisionsOfAccountingStandard ChangesOtherThanOnesBasedOnRevisionsOfAccountingStandard ChangesInAccountingEstimates RetrospectiveRestatement NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock NumberOfTreasuryStockAtTheEndOfFiscalYear AverageNumberOfShares
0 20161207453651.00 20170104_2753 2017-01-04 2753.00 2017-01-04 07:30:00 1483482600.00 3QFinancialStatements_Consolidated_JP 2016-12-31 3Q ... 319.76 NaN False True False False False 6848800.00 6848800.00
1 20170104467704.00 20170104_3353 2017-01-04 3353.00 2017-01-04 15:00:00 1483509600.00 3QFinancialStatements_Consolidated_JP 2016-11-30 3Q ... 485.36 NaN False True False False False 2035000.00 118917 1916083.00
2 20161229465838.00 20170104_4575 2017-01-04 4575.00 2017-01-04 12:00:00 1483498800.00 ForecastRevision 2016-12-31 2Q ... -93.11 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 20170104467878.00 20170105_2659 2017-01-05 2659.00 2017-01-05 15:00:00 1483596000.00 3QFinancialStatements_Consolidated_JP 2016-11-30 3Q ... 285.05 NaN False True False False False 31981654.00 18257 31963405.00
4 20170105468518.00 20170105_3050 2017-01-05 3050.00 2017-01-05 15:30:00 1483597800.00 ForecastRevision 2017-02-28 FY ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 45 columns

这些**基本面数据**能够反映公司的运营情况和盈利能力,是评估股票价值的重要依据。

【思考】:为什么除了股票数据以外还要提供那么多基本面财报数据?

股票的价格不仅受到市场供求的影响,还与公司的基本面、行业走势、宏观经济等因素密切相关。提供这些数据可以让我们从多个角度分析股票,构建更为全面和准确的预测模型。

【附录】字段含义详解

“stock_prices.csv"和"secondary_stock_prices.csv” 文件中的各个字段的中文解释:

  • RowId:行ID,每一行数据的唯一标识符。
  • Date:日期,格式为 “YYYY-MM-DD”,代表这一行数据的日期。
  • SecuritiesCode:证券代码,每个上市公司的唯一标识符。
  • Open:开盘价,即该股票在这一天开盘时的价格。
  • High:最高价,即该股票在这一天的交易过程中的最高价格。
  • Low:最低价,即该股票在这一天的交易过程中的最低价格。
  • Close:收盘价,即该股票在这一天收盘时的价格。
  • Volume:成交量,即在这一天中,这个股票的所有交易的股票数量总和。
  • AdjustmentFactor:调整因子,用于调整股票价格和成交量,以反映股票分割、股息等事件的影响。
  • ExpectedDividend:预期股息,即公司预期将在未来发放的每股股息。
  • SupervisionFlag:监管标志,如果这个字段为 “1”,则表示该股票在这一天被监管。
  • Target:目标,即该股票在下一天的回报率。

“stock_list.csv” 文件中的各个字段的中文解释:

  • SecuritiesCode:证券代码,用于在交易所中唯一标识每支股票。每个证券代码都与特定的公司或资产关联。
  • EffectiveDate:生效日期,指的是某一特定信息或规则在股票交易中的生效时间。例如,股票上市日期、股票分红的生效日期等。
  • Name:股票名称,公司或证券的全名。
  • Section/Products:板块/产品类别,表示股票所属的交易部分或类别。
  • NewMarketSegment:新市场细分,可能涉及股票的市场细分。
  • 33SectorCode 和 33SectorName:
  1. 描述:33SectorCode和33SectorName是针对日本东证(东京证券交易所)上市的所有国内普通股(TOPIX的成份股)进行的行业划分。这个划分将股票按照33个传统行业分类,如水产和农林业、矿业、建筑业等。33SectorCode是每个传统行业的唯一编码,而33SectorName是对应行业的名称。
  2. 应用:通过这33个行业的分类,可以更准确地分析和理解股市的整体走势、行业趋势和具体公司的表现。这为投资者和分析师提供了有针对性的分析和决策工具。
  • 17SectorCode 和 17SectorName:
  1. 描述:17SectorCode和17SectorName是基于日本股票市场33个传统行业的重新划分。JPX为了投资方便,将33个传统行业整合和重新组合为17个新行业,形成了TOPIX-17系列。每个新行业由一个或多个传统行业组成,例如,TOPIX-17食品由“水产和农林业”和“食品”行业组成。17SectorCode是每个新划分行业的唯一编码,而17SectorName是对应行业的名称。
  2. 应用:这17个行业的重新划分更符合现代投资和分析的需求,有助于更精确地反映市场趋势和提供更灵活的投资策略。对于长期投资和策略分析,这种精细的行业重新组合提供了更深入的见解。

获取更详细的信息,可以参考jpx的官方说明

  • NewIndexSeriesSizeCode:新指数系列规模代码,可能涉及股票的指数分类代码。
  • NewIndexSeriesSize:新指数系列规模,可能涉及股票的指数分类。
    • 股票指数是由多支股票组成的组合,用于衡量特定股票市场或市场部分的表现。新指数系列规模可能是一种特定的指数分类方法,用于将股票按照市值、流动性或其他标准分组。新指数系列规模代码可能是该分类方法的编码表示。
  • TradeDate:交易日期,股票的交易日期。
  • Close:收盘价,股票当天的收盘价格。
  • IssuedShares:已发行股份,公司已发行的股份数量。
  • MarketCapitalization:市值,股票的市场资本化,即股票的总市值。
  • Universe0:布尔值,股票是否属于2000支需要预测股票的集合。

这是 “options.csv” 文件中的各个字段的中文解释:

  • DateCode:日期代码,格式为 “YYYYMMDD”,代表这一行数据的日期。
  • Date:日期,格式为 “YYYY-MM-DD”,代表这一行数据的日期。
  • OptionsCode:期权代码,每个期权的唯一标识符。
  • WholeDayOpen:全天开盘价,即该期权在这一天开盘时的价格。
  • WholeDayHigh:全天最高价,即该期权在这一天的交易过程中的最高价格。
  • WholeDayLow:全天最低价,即该期权在这一天的交易过程中的最低价格。
  • WholeDayClose:全天收盘价,即该期权在这一天收盘时的价格。
  • NightSessionOpen:夜间开盘价,即该期权在夜间交易开始时的价格。
  • NightSessionHigh:夜间最高价,即该期权在夜间交易过程中的最高价格。
  • NightSessionLow:夜间最低价,即该期权在夜间交易过程中的最低价格。
  • NightSessionClose:夜间收盘价,即该期权在夜间交易结束时的价格。
  • DaySessionOpen:日间开盘价,即该期权在日间交易开始时的价格。
  • DaySessionHigh:日间最高价,即该期权在日间交易过程中的最高价格。
  • DaySessionLow:日间最低价,即该期权在日间交易过程中的最低价格。
  • DaySessionClose:日间收盘价,即该期权在日间交易结束时的价格。
  • TradingVolume:交易量,即在这一天中,这个期权的所有交易的期权合约数量总和。
  • OpenInterest:未平仓合约,即在这一天结束时,还未平仓的期权合约的数量。
  • TradingValue:交易价值,即这一天中所有的期权交易的总价值。
  • ContractMonth:合约月份,格式为 “YYYYMM”,代表这个期权合约的到期月份。
  • StrikePrice:执行价格,即期权行权时的预定价格。
  • WholeDayVolume:全天交易量,即在这一天中,这个期权的所有交易的期权合约数量总和。
  • Putcall:期权类型,“1” 代表看跌期权,“2” 代表看涨期权。
  • LastTradingDay:最后交易日,即这个期权合约的最后交易日。
  • SpecialQuotationDay:特别报价日,即计算这个期权合约结算价的日子。
  • SettlementPrice:结算价,即这个期权合约的结算价格。
  • TheoreticalPrice:理论价格,即根据期权定价模型计算出的这个期权的理论价格。
  • BaseVolatility:基础波动率,即在计算理论价格时使用的波动率。
  • ImpliedVolatility:隐含波动率,即根据市场价格反推出的期权波动率。
  • InterestRate:利率,即在计算理论价格时使用的无风险利率。
  • DividendRate:股息率,即在计算理论价格时使用的预期股息率。
  • Dividend:股息,即期权行权时预期的股息。

这是 “financials.csv” 文件中的各个字段的中文解释:

  • DisclosureNumber:公告号,每一份财务报告的唯一标识符。
  • DateCode:日期代码,格式为 “YYYYMMDD”,代表这一行数据的日期。
  • Date:日期,格式为 “YYYY-MM-DD”,代表这一行数据的日期。
  • SecuritiesCode:证券代码,每个上市公司的唯一标识符。
  • DisclosedDate:披露日期,即这份财务报告公布的日期。
  • DisclosedTime:披露时间,即这份财务报告公布的具体时间。
  • DisclosedUnixTime:披露Unix时间,即这份财务报告公布的时间(以Unix时间表示)。
  • TypeOfDocument:文件类型,表示这份财务报告的类型。
  • CurrentPeriodEndDate:当前期末日期,即这份财务报告所覆盖的期末日期。
  • TypeOfCurrentPeriod:当前期类型,表示这份财务报告所覆盖的财务期间类型(如第一季度、第二季度等)。
  • CurrentFiscalYearStartDate:当前财年开始日期,即这份财务报告所覆盖的财年的开始日期。
  • CurrentFiscalYearEndDate:当前财年结束日期,即这份财务报告所覆盖的财年的结束日期。
  • NetSales:净销售额,即公司在这个财务期间的销售总额。
  • OperatingProfit:营业利润,即公司在这个财务期间的营业收入减去营业成本后的利润。
  • OrdinaryProfit:常规利润,即公司在这个财务期间的常规业务收入减去常规业务成本后的利润。
  • Profit:利润,即公司在这个财务期间的总利润。
  • EarningsPerShare:每股收益,即公司在这个财务期间的利润除以总发行股票数量后的值。
  • TotalAssets:总资产,即公司在这个财务期间的所有资产的总和。
  • Equity:股权,即公司在这个财务期间的所有股东权益的总和。
  • EquityToAssetRatio:权益资产比率,即公司在这个财务期间的股东权益除以总资产后的比率。
  • BookValuePerShare:每股账面价值,即公司在这个财务期间的股东权益除以总发行股票数量后的值。
  • ResultDividendPerShare1stQuarter:第一季度的每股股息,即公司在这个财务期间的第一季度每股发放的股息。
  • ResultDividendPerShare2ndQuarter:第二季度的每股股息,即公司在这个财务期间的第二季度每股发放的股息。
  • ResultDividendPerShare3rdQuarter:第三季度的每股股息,即公司在这个财务期间的第三季度每股发放的股息。
  • ResultDividendPerShareFiscalYearEnd:财年末的每股股息,即公司在这个财务期间的财年末每股发放的股息。
  • ResultDividendPerShareAnnual:全年的每股股息,即公司在这个财务期间的全年每股发放的股息。
  • ForecastDividendPerShare1stQuarter:预计第一季度的每股股息,即公司预计在下一个财务期间的第一季度每股发放的股息。
  • ForecastDividendPerShare2ndQuarter:预计第二季度的每股股息,即公司预计在下一个财务期间的第二季度每股发放的股息。
  • ForecastDividendPerShare3rdQuarter:预计第三季度的每股股息,即公司预计在下一个财务期间的第三季度每股发放的股息。
  • ForecastDividendPerShareFiscalYearEnd:预计财年末的每股股息,即公司预计在下一个财务期间的财年末每股发放的股息。
  • ForecastDividendPerShareAnnual:预计全年的每股股息,即公司预计在下一个财务期间的全年每股发放的股息。
  • ForecastNetSales:预计净销售额,即公司预计在下一个财务期间的销售总额。
  • ForecastOperatingProfit:预计营业利润,即公司预计在下一个财务期间的营业收入减去营业成本后的利润。
  • ForecastOrdinaryProfit:预计常规利润,即公司预计在下一个财务期间的常规业务收入减去常规业务成本后的利润。
  • ForecastProfit:预计利润,即公司预计在下一个财务期间的总利润。
  • ForecastEarningsPerShare:预计每股收益,即公司预计在下一个财务期间的利润除以总发行股票数量后的值。
  • ApplyingOfSpecificAccountingOfTheQuarterlyFinancialStatements:应用特定的季度财务报告会计方法,表示公司是否在这个财务期间应用了特定的季度财务报告会计方法。
  • MaterialChangesInSubsidiaries:子公司的重大变化,表示公司在这个财务期间是否发生了子公司的重大变化。
  • ChangesBasedOnRevisionsOfAccountingStandard:基于会计标准修订的变化,表示公司在这个财务期间是否根据会计标准的修订进行了变化。
  • ChangesOtherThanOnesBasedOnRevisionsOfAccountingStandard:基于会计标准修订之外的变化,表示公司在这个财务期间是否进行了基于会计标准修订之外的变化。
  • ChangesInAccountingEstimates:会计估计的变化,表示公司在这个财务期间是否进行了会计估计的变化。
  • RetrospectiveRestatement:追溯重述,表示公司在这个财务期间是否进行了追溯重述。
  • NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock:包括库存股在内的财年末已发行和未流通股份的数量。
  • NumberOfTreasuryStockAtTheEndOfFiscalYear:财年末的库存股数量。
  • AverageNumberOfShares:平均股份数量,即公司在这个财务期间的平均发行股票数量。

这是 “trades.csv” 文件中的各个字段的中文解释:

  • PublishedDate:发布日期,通常是交易周的第一个星期四。
  • StartDate:开始日期,这个交易周的第一个交易日。
  • EndDate:结束日期,这个交易周的最后一个交易日。
  • Section:市场部门名称。
  • TotalSales:总销售额,包括自营商和经纪商的销售额。
  • TotalPurchases:总购买额,包括自营商和经纪商的购买额。
  • TotalTotal:总交易额,即总销售额和总购买额的总和。
  • TotalBalance:总平衡,即总销售额和总购买额的差额。
  • ProprietarySales:自营商的销售额。
  • ProprietaryPurchases:自营商的购买额。
  • ProprietaryTotal:自营商的交易额,即自营商的销售额和购买额的总和。
  • ProprietaryBalance:自营商的平衡,即自营商的销售额和购买额的差额。
  • BrokerageSales:经纪商的销售额。
  • BrokeragePurchases:经纪商的购买额。
  • BrokerageTotal:经纪商的交易额,即经纪商的销售额和购买额的总和。
  • BrokerageBalance:经纪商的平衡,即经纪商的销售额和购买额的差额。
  • IndividualsSales:个人的销售额。
  • IndividualsPurchases:个人的购买额。
  • IndividualsTotal:个人的交易额,即个人的销售额和购买额的总和。
  • IndividualsBalance:个人的平衡,即个人的销售额和购买额的差额。
  • ForeignersSales:外国人的销售额。
  • ForeignersPurchases:外国人的购买额。
  • ForeignersTotal:外国人的交易额,即外国人的销售额和购买额的总和。
  • ForeignersBalance:外国人的平衡,即外国人的销售额和购买额的差额。
  • SecuritiesCosSales:证券公司的销售额。
  • SecuritiesCosPurchases:证券公司的购买额。
  • SecuritiesCosTotal:证券公司的交易额,即证券公司的销售额和购买额的总和。
  • SecuritiesCosBalance:证券公司的平衡,即证券公司的销售额和购买额的差额。
  • InvestmentTrustsSales:投资信托的销售额。
  • InvestmentTrustsPurchases:投资信托的购买额。
  • InvestmentTrustsTotal:投资信托的交易额,即投资信托的销售额和购买额的总和。
  • InvestmentTrustsBalance:投资信托的平衡,即投资信托的销售额和购买额的差额。
  • BusinessCosSales:商业公司的销售额。
  • BusinessCosPurchases:商业公司的购买额。
  • BusinessCosTotal:商业公司的交易额,即商业公司的销售额和购买额的总和。
  • BusinessCosBalance:商业公司的平衡,即商业公司的销售额和购买额的差额。
  • OtherInstitutionsSales:其他机构的销售额。
  • OtherInstitutionsPurchases:其他机构的购买额。
  • OtherInstitutionsTotal:其他机构的交易额,即其他机构的销售额和购买额的总和。
  • OtherInstitutionsBalance:其他机构的平衡,即其他机构的销售额和购买额的差额。
  • InsuranceCosSales:保险公司的销售额。
  • InsuranceCosPurchases:保险公司的购买额。
  • InsuranceCosTotal:保险公司的交易额,即保险公司的销售额和购买额的总和。
  • InsuranceCosBalance:保险公司的平衡,即保险公司的销售额和购买额的差额。
  • CityBKsRegionalBKsEtcSales:城市银行、地区银行等的销售额。
  • CityBKsRegionalBKsEtcPurchases:城市银行、地区银行等的购买额。
  • CityBKsRegionalBKsEtcTotal:城市银行、地区银行等的交易额,即城市银行、地区银行等的销售额和购买额的总和。
  • CityBKsRegionalBKsEtcBalance:城市银行、地区银行等的平衡,即城市银行、地区银行等的销售额和购买额的差额。
  • TrustBanksSales:信托银行的销售额。
  • TrustBanksPurchases:信托银行的购买额。
  • TrustBanksTotal:信托银行的交易额,即信托银行的销售额和购买额的总和。
  • TrustBanksBalance:信托银行的平衡,即信托银行的销售额和购买额的差额。
  • OtherFinancialInstitutionsSales:其他金融机构的销售额。
  • OtherFinancialInstitutionsPurchases:其他金融机构的购买额。
  • OtherFinancialInstitutionsTotal:其他金融机构的交易额,即其他金融机构的销售额和购买额的总和。
  • OtherFinancialInstitutionsBalance:其他金融机构的平衡,即其他金融机构的销售额和购买额的差额。

2.3 baseline的建立

请在kaggle的notebook上运行下列代码,不要在本地运行:

在kaggle上,新建一个notebook,都会有一个这样的cell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
for filename in filenames:
print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All"
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
/kaggle/input/jpx-tokyo-stock-exchange-prediction/stock_list.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/example_test_files/sample_submission.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/example_test_files/options.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/example_test_files/financials.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/example_test_files/secondary_stock_prices.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/example_test_files/trades.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/example_test_files/stock_prices.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/jpx_tokyo_market_prediction/competition.cpython-37m-x86_64-linux-gnu.so
/kaggle/input/jpx-tokyo-stock-exchange-prediction/jpx_tokyo_market_prediction/__init__.py
/kaggle/input/jpx-tokyo-stock-exchange-prediction/data_specifications/stock_fin_spec.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/data_specifications/trades_spec.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/data_specifications/stock_price_spec.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/data_specifications/options_spec.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/data_specifications/stock_list_spec.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/train_files/options.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/train_files/financials.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/train_files/secondary_stock_prices.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/train_files/trades.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/train_files/stock_prices.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/supplemental_files/options.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/supplemental_files/financials.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/supplemental_files/secondary_stock_prices.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/supplemental_files/trades.csv
/kaggle/input/jpx-tokyo-stock-exchange-prediction/supplemental_files/stock_prices.csv

运行这个cell,notebook会利用os.walk打印出input目录下所有文件,如下图所示:

image

1
2
3
4
5
6
7
8
9
# 导入模型
import lightgbm as lgb

# 导入joblib库,用于保存和加载模型
import joblib
# 导入stats库,用于进行数据标准化
from scipy import stats
# 导入比赛提供的API
import jpx_tokyo_market_prediction # 注意这行代码每一次重启kernels只能运行一次
1
2
3
4
5
6
7
8
9
10
11
# 加载数据集

# 读取主要的股价数据
stock_prices = pd.read_csv("../input/jpx-tokyo-stock-exchange-prediction/train_files/stock_prices.csv")
# 读取补充的主要股价数据
supplemental_prices = pd.read_csv("../input/jpx-tokyo-stock-exchange-prediction/supplemental_files/stock_prices.csv")

# 合并所有的股价数据

# 股价数据与补充的股价数据进行合并
stock_prices = stock_prices.append(supplemental_prices)
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
# 定义为训练数据进行特征工程的函数
def featuring_train(data):
'''
对训练集数据进行处理:
将日期转换为datetime格式;
使用0来填充ExpectedDividend和Target中的缺失值;
将SupervisionFlag从布尔型转换为整数型;
填充Open、High、Low和Close列中的缺失值;
删除了不需要的RowId列。
'''
# 将日期从字符串格式转换为datetime格式
data['Date'] = pd.to_datetime(data['Date'])
# 使用0来填充ExpectedDividend和Target中的缺失值
data['ExpectedDividend'] = data['ExpectedDividend'].fillna(0)
data['Target'] = data['Target'].fillna(0)
# 将SupervisionFlag从布尔型转换为整数型(True转为1,False转为0)
data["SupervisionFlag"] = data["SupervisionFlag"].astype(int)

# 填充Open、High、Low和Close列中的缺失值
cols = ['Open', 'High', 'Low', 'Close']
data.loc[:,cols] = data.loc[:,cols].ffill().bfill()

# 删除不需要的列RowId
data = data.drop(['RowId'], axis=1)

return data
1
2
# 对数据进行特征工程
data = featuring_train(stock_prices)
1
2
# 删除不需要的Date列,得到训练数据集
data_train = data.drop(['Date'], axis=1)
1
2
3
# 从数据中分离学习特征和目标变量
X_train = data_train.drop(['Target'], axis=1)
y_train = data_train['Target']
1
2
# 显示前几行数据
X_train.head()
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
SecuritiesCode Open High Low Close Volume AdjustmentFactor ExpectedDividend SupervisionFlag
0 1301 2734.0 2755.0 2730.0 2742.0 31400 1.0 0.0 0
1 1332 568.0 576.0 563.0 571.0 2798500 1.0 0.0 0
2 1333 3150.0 3210.0 3140.0 3210.0 270800 1.0 0.0 0
3 1376 1510.0 1550.0 1510.0 1550.0 11300 1.0 0.0 0
4 1377 3270.0 3350.0 3270.0 3330.0 150800 1.0 0.0 0
1
2
3
4
5
6
7
# 实例化模型

# 使用LightGBM模型
lgb_model = lgb.LGBMRegressor(seed=1412)

# 使用原始数据训练LightGBM模型
lgb_model.fit(X_train, y_train)
LGBMRegressor(seed=1412)
1
2
3
# 获取特征名称列表
features = list(X_train.columns)
features
['SecuritiesCode',
 'Open',
 'High',
 'Low',
 'Close',
 'Volume',
 'AdjustmentFactor',
 'ExpectedDividend',
 'SupervisionFlag']
1
2
3
4
5
6
# 打印LightGBM模型的特征重要性
print("LightGBM Model Feature Importance:")
feature_importance_lgb = lgb_model.feature_importances_
sorted_indices_lgb = np.argsort(feature_importance_lgb)[::-1] # 按重要性降序排序的索引
for idx in sorted_indices_lgb:
print(f"Feature: {features[idx]}, Importance: {feature_importance_lgb[idx]}")
LightGBM Model Feature Importance:
Feature: Volume, Importance: 789
Feature: SecuritiesCode, Importance: 661
Feature: High, Importance: 310
Feature: Low, Importance: 300
Feature: Open, Importance: 298
Feature: Close, Importance: 294
Feature: ExpectedDividend, Importance: 282
Feature: SupervisionFlag, Importance: 51
Feature: AdjustmentFactor, Importance: 15

从特征重要性可以看到成交量Volume是最重要的,从业务的角度来说,很难想象成交量和股价及收益率之间的关系。

1
2
3
4
5
6
7
8
9
10
11
12
# 对测试数据进行特征工程,和featuring_train差不多,区别是不需要处理Target
def featuring_test(data):
# 填充缺失的预期股息为0
data['ExpectedDividend'] = data['ExpectedDividend'].fillna(0)
# 将布尔型的监管标志转换为整数
data["SupervisionFlag"] = data["SupervisionFlag"].astype(int)

# 填充缺失的价格数据
cols = ['Open', 'High', 'Low', 'Close']
data.loc[:,cols] = data.loc[:,cols].ffill().bfill()

return data
1
2
3
4
5
6
# 注意这段代码每一次重启kernels只能运行一次

# 初始化API环境,大多数code game语法都有类似的语法
env = jpx_tokyo_market_prediction.make_env()
# 获取迭代器,用于逐步获取测试数据
iter_test = env.iter_test()
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 初始化计数器,用于后续判断是否为第一次迭代
count=0

# 定义我们要使用的列
cols = ['Date', 'RowId', 'SecuritiesCode', 'Open', 'High', 'Low', 'Close','Volume', 'AdjustmentFactor', 'ExpectedDividend', 'SupervisionFlag']

# 开始循环处理测试数据
# for循环可以从iter_test中或许测试集的信息
# 在这个比赛中可以获取到6张表格文件
for (prices, options, financials, trades, secondary_prices, sample_prediction) in iter_test:

# 获取当前测试数据的日期
current_date = prices['Date'].unique()[0]

# 第一次迭代时,确保训练数据不包含测试数据,以避免数据泄露
if count == 0:
sub_df = stock_prices[cols]
sub_df = sub_df.loc[(sub_df['Date']<current_date)]
count = 1

# 将当前测试数据追加到sub_df
sub_df = sub_df.append(prices)
# 删除重复的数据(因为我们不确定prices里面是否包含过去的数据)
sub_df = sub_df.drop_duplicates()
# 按证券代码和日期对数据进行排序
sub_df.sort_values(by=['SecuritiesCode', 'Date'], inplace=True)
# 重置索引
sub_df.reset_index(drop=True, inplace=True)

# 进行特征工程处理
x_test = featuring_test(sub_df)
# 仅选择当前日期的数据进行预测
x_test = x_test.loc[(x_test['Date'] == current_date)]
# 删除不需要的列
x_test = x_test.drop(['RowId', 'Date'], axis=1)

# 使用线性回归和lightgbm模型进行预测
y_pred = lgb_model.predict(x_test) # 单模夏普比率0.123

# 将预测值赋给目标列
sample_prediction['Target'] = y_pred

# 按预测值进行降序排列
sample_prediction = sample_prediction.sort_values(by = "Target", ascending = False)
# 为每个数据分配一个排名
sample_prediction['Rank'] = np.arange(len(sample_prediction.index))
# 按证券代码进行升序排列
sample_prediction = sample_prediction.sort_values(by = "SecuritiesCode", ascending = True)
# 删除不需要的目标列
sample_prediction.drop(["Target"], axis = 1)
# 提取所需的列为提交数据
submission = sample_prediction[["Date", "SecuritiesCode", "Rank"]]

# 确保预测数据的完整性和连续性
assert sample_prediction["Rank"].notna().all()
assert sample_prediction["Rank"].min() == 0
assert sample_prediction["Rank"].max() == len(sample_prediction["Rank"]) - 1

# 提交预测结果(code game的固定语法)
env.predict(submission)
This version of the API is not optimized and should not be used to estimate the runtime of your code on the hidden test set.


/opt/conda/lib/python3.7/site-packages/ipykernel_launcher.py:12: FutureWarning: Inferring datetime64[ns] from data containing strings is deprecated and will be removed in a future version. To retain the old behavior explicitly pass Series(data, dtype={value.dtype})
  if sys.path[0] == "":
/opt/conda/lib/python3.7/site-packages/ipykernel_launcher.py:12: FutureWarning: Inferring datetime64[ns] from data containing strings is deprecated and will be removed in a future version. To retain the old behavior explicitly pass Series(data, dtype={value.dtype})
  if sys.path[0] == "":
1
prices.columns
Index(['Date', 'RowId', 'SecuritiesCode', 'Open', 'High', 'Low', 'Close',
       'Volume', 'AdjustmentFactor', 'ExpectedDividend', 'SupervisionFlag'],
      dtype='object')
1
2
# 查看读取出来的测试集日期范围
prices['Date'].unique()
array(['2021-12-07'], dtype=object)
1
2
# 查看提交文件格式
print(submission)
            Date  SecuritiesCode  Rank
0     2021-12-07            1301  1421
1     2021-12-07            1332  1782
2     2021-12-07            1333   339
3     2021-12-07            1375  1236
4     2021-12-07            1376   742
...          ...             ...   ...
1995  2021-12-07            9990   757
1996  2021-12-07            9991   557
1997  2021-12-07            9993   100
1998  2021-12-07            9994   188
1999  2021-12-07            9997   720

[2000 rows x 3 columns]
1
2
# 查看我们的提交数据
sample_prediction
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
Date SecuritiesCode Rank Target
0 2021-12-07 1301 1421 -0.002150
1 2021-12-07 1332 1782 -0.005311
2 2021-12-07 1333 339 0.001276
3 2021-12-07 1375 1236 -0.001467
4 2021-12-07 1376 742 -0.000261
... ... ... ... ...
1995 2021-12-07 9990 757 -0.000289
1996 2021-12-07 9991 557 0.000226
1997 2021-12-07 9993 100 0.006162
1998 2021-12-07 9994 188 0.003174
1999 2021-12-07 9997 720 -0.000191

2000 rows × 4 columns

Powered by Hexo & Theme Keep
This site is deployed on