案例:金融数据分析----code详解版
1.引言
金融案例分析是金融领域研究的重要组成部分,可以帮助人们深入理解金融市场中的运作和交易流程,分析金融风险和机会,制定投资和风险管理策略等。本篇文章将以Python为工具,介绍一种基于Python的金融案例分析方法,以帮助读者更好地理解金融领域知识。
1.1案例分析目标
本篇文章的案例分析目标是,以股票市场为例,介绍如何利用Python进行金融数据分析。具体包括:
- 1.利用Python获取股票数据;
- 2.对股票数据进行数据清洗和处理;
- 3.利用Python进行股票数据可视化;
- 4.利用Python进行股票市场分析和预测。
1.2涉及知识点
本篇文章的案例分析将涉及以下知识点:
- 1.Python基础语法;
- 2.数据清洗和处理;
- 3.数据可视化;
- 4.机器学习基础。
1.3案例分析流程
本篇文章的案例分析流程包括以下步骤:
- 1.加载包:代码的第一部分是加载数据分析过程中所需要的包,具体加载方法可参见2.4.4包的导入
- 2.数据获取:利用Python获取股票数据,并对数据进行存取,数据的存取见3.python数据的存取;
- 3.数据清洗和处理:对股票数据进行数据清洗和处理,包括缺失值处理、异常值处理、数据标准化等,常用的操作命令见5.dataframe数据清洗;
- 3.数据可视化:利用Python进行股票数据可视化,相关知识点见8.dataframe数据可视化;
- 4.建立模型:利用Python建立股票市场分析和预测模型;
- 5.模型评估和优化:对模型进行评估和优化;
- 6.结论和展望:对案例分析结果进行结论和展望。
下面将分别介绍每个步骤的具体实现。
2.数据获取
涉及知识点:
- 第三方包的安装2.4.3包的安装
- 包或模块的加载2.4.4包的导入
- 查看变量类型1.1查看变量类型:type(变量名)
- 查看dataframe数据3.1查看前/后n行
- 保存dataframe数据3.1.4 保存Dataframe
2.1安装tushare库
首先需要安装tushare库。可以在命令行中运行以下命令来安装:
pip install tushare
2.2获取Token
使用tushare需要申请Token,申请方法如下:
访问tushare官网,注册账号并登录
在个人中心页面获取token
2.3导入tushare库并设置Token
导入tushare库并设置Token,代码如下:
import tushare as ts
ts.set_token('你的Token')
2.4获取数据
以获取中国平安(601318.SH)2015年至今的股票数据为例,代码如下:
import tushare as ts #加载包
import pandas as pd
import numpy as np
from pylab import mpl
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
ts.set_token('填入你自己的Token!!!!!')# 设置Token
pro = ts.pro_api()# 初始化pro接口
df = pro.daily(ts_code='601318.SH', start_date='20150101')# 获取中国平安(601318.SH)2015年至今的股票数据
print(df.head())# 打印前5行数据
以上代码实现的功能是从tushare获取中国平安(601318.SH)2015年至今的股票数据,并打印前5行数据。
2.6保存数据
df.to_csv('./data/sh601318.csv', index=False)
- 需要注意的是,tushare的数据接口有一定的限制,包括每天请求次数和请求数据量等。如果需要更多数据,可以考虑购买tushare的付费服务。
3.数据预处理
涉及知识点:
在获取了股票数据后,我们需要对数据进行清洗和处理,以保证后续分析的准确性。具体实现代码如下:
3.1读取已有数据
import pandas as pd
# 读取CSV文件
df = pd.read_csv('./data/sh601318.csv')
# 查看数据基本信息
print(df.info())
3.2清理数据
#删除不需要的列
df.drop(['ts_code'], axis=1, inplace=True) # 删除code列
#重命名dataframe数据列名
df.rename(columns={'trade_date':'Date', 'open':'Open', 'close':'Close', 'high':'High', 'low':'Low', 'volume':'Volume'}, inplace=True) # 重命名列
#将dataframe时间列由int转换为datetime格式
df['Date'] = pd.to_datetime(df['Date'], format='%Y%m%d') # 将Date列转换为datetime格式
#设置索引列
df.set_index('Date', inplace=True) # 将Date列设为行索引
#按指定列的升序/降序排列
df.sort_index(inplace=True) # 按日期排序
#保存已处理完毕的数据
df.to_csv('./data/sh601318_processed.csv')
# 从某dataframe中提取一个只包含某些列的子数据框
df_601318 = df[['Close']]
df_601318.head()
如果是下次重新分析,可考虑通过读取已处理好的数据开始后续分析:
import pandas as pd
df = pd.read_csv('./data/sh601318_processed.csv')
df_601318 = df[['Close']]
# 将Date列转换为datetime格式
df_601318.index = pd.to_datetime(df_601318.index, format='%Y-%m-%d')
print(df_601318.head())
4.数据可视化
涉及知识点:
- 3.2选择DataFrame中的某列/某行:
- dataframe数据可视化8.2使用matplotlib库
- 绘图时中文字符及其它设定 2.plt.rcParams属性总结 2.font.family 9.axes.unicode_minus
数据可视化是金融分析的重要手段,可以帮助我们更好地理解数据特征和趋势。在Python中,dataframe数据的可视化可参见(dataframe数据可视化)。具体实现代码如下:
import matplotlib.pyplot as plt
# 绘制中国平安收盘价折线图
plt.figure(figsize=(15, 6))
plt.plot(df_601318['Close'])
plt.title('ZGPA Closing Prices')
plt.xlabel('Date')
plt.ylabel('Price')
plt.show()
上述绘图中的图标题,以及横纵坐标或图例的标签均是英文,若将其直接改成中文则无法显示,需要设置绘制时的字符,具体实现如下:
# 如前面已加载过相同的包,此处可省略下面包的加载代码
import matplotlib.pyplot as plt
#中文字符设定 plt.rcParams属性总结
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
# 绘制中国平安收盘价折线图
plt.figure(figsize=(15, 6))
plt.plot(df_601318['Close'])
plt.title('中国平安收盘价格')
plt.xlabel('日期')
plt.ylabel('收盘价')
plt.show()
5.基本走势分析
涉及知识点:
- 描述性统计分析6.2统计运算
- 移动平均\标准差计算7.6移动窗口函数
- dataframe数据可视化8.2使用matplotlib库
- 绘图时中文字符及其它设定 2.plt.rcParams属性总结 2.font.family 9.axes.unicode_minus
5.1描述性统计分析
进行金融案例分析时,描述性统计分析是非常重要的,它可以帮助我们更好地理解数据的基本特征和趋势。在Python中,我们可以使用pandas库来进行描述性统计分析。具体实现代码如下:
# 描述性统计分析
print(df_601318.describe())
5.2历史走势分析
- 计算每日收益率
# 计算每日收益率
df_601318['daily_return'] = df_601318['Close'].pct_change()
df.pct_change()
是一个Pandas DataFrame方法,用于计算每个元素与前一个元素之间的百分比变化。用股票收盘价计算得到的便是某股票的每日收益率,其数学表达式为:
(
d
f
−
d
f
.
s
h
i
f
t
(
1
)
)
/
d
f
.
s
h
i
f
t
(
1
)
(df - df.shift(1)) / df.shift(1)
(df−df.shift(1))/df.shift(1)
其中, d f . s h i f t ( 1 ) df.shift(1) df.shift(1)表示将DataFrame向下移动1行,以便计算当前元素与前一个元素之间的变化。然后,用当前DataFrame减去向下移动后的DataFrame,再除以向下移动后DataFrame,即可得到每个元素与前一个元素之间的百分比变化。
- 计算移动平均收盘价
# 计算移动平均线
df_601318['ma5'] = df_601318['Close'].rolling(5).mean()#周平均收盘价
df_601318['ma10'] = df_601318['Close'].rolling(10).mean()#半月平均收盘价
df_601318['ma20'] = df_601318['Close'].rolling(20).mean()#月平均收盘价
- 计算月平均收益率的布林带
# 计算月平均收益率的布林带-95%的置信区间
df_601318['std20'] = df_601318['Close'].rolling(20).std()
df_601318['upper_band20'] = df_601318['ma20'] + 2 * df_601318['std20']
df_601318['lower_band20'] = df_601318['ma20'] - 2 * df_601318['std20']
- 可视化操作
#中文字符设定
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
plt.rcParams 的属性是全局生效的,因此一般建议在程序开头设置一次即可
。
# 绘制收益率曲线
df_601318['daily_return'].plot(figsize=(10,6))
plt.title('中国平安日度收益率')
plt.xlabel('日期')
plt.ylabel('收益率')
plt.show()
# 绘制收益率的直方图
df_601318['daily_return'].hist(bins=50, figsize=(10,6))
plt.title('Distribution of Daily Return of ZGPA')
plt.xlabel('Return')
plt.ylabel('Frequency')
plt.show()
# 绘制移动平均线和布林带
plt.figure(figsize=(10,6))
plt.plot(df_601318['Close'])
plt.plot(df_601318['ma5'])
plt.plot(df_601318['ma10'])
plt.plot(df_601318['ma20'])
plt.plot(df_601318['upper_band20'])
plt.plot(df_601318['lower_band20'])
plt.title('Moving Averages and Bollinger Bands of ZGPA')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend(['Close', 'MA5', 'MA10', 'MA20', 'Upper Band20', 'Lower Band20'])
plt.show()
6.周期效应分析
涉及知识点:
- 提取datetime索引的周期信息 7.11提取年、月、日等信息 1.
- 分组并求均值6.4聚合运算 (1)groupby运算
- 均值T检验3.2 独立样本 t 检验
基于上述数据,我们可以进行周期效应分析。在Python中,我们可以使用pandas库和statsmodels库来进行周期效应分析。具体实现代码如下:
6.1周度效应
6.1.1整体周效应分析
- Step1:整体周效应计算
# 将数据添加周标记
df_601318['Weekday']=df_601318.index.weekday
#全部数据按周几分组并计算周一至周五的平均收盘价
weekday_mean=df_601318.groupby('Weekday')['Close'].mean()
#周一至周五的均值与整体均值的比值
weekday_mean_ratio = weekday_mean / weekday_mean.mean()
#大于1表示高于均值,小于1低于均值
print(weekday_mean_ratio)
- Step2:整体周效应可视化
#绘制折线图
plt.figure(figsize=(10, 6))
plt.plot(weekday_mean_ratio.values)
plt.axhline(1.0, color='gray', linewidth=1)
plt.xticks(range(5), ['星期一', '星期二', '星期三', '星期四', '星期五'])
plt.title('中国平安周效应分析')
plt.xlabel('星期')
plt.ylabel('平均收盘价比值')
plt.show()
- Step3:整体周效应均值检验
from scipy.stats import ttest_ind
# 计算整体周效应的t检验 周一与周五有无显著差异
weekday_0 = df_601318[df_601318['Weekday'] == 0]['Close']
weekday_4 = df_601318[df_601318['Weekday'] == 4]['Close']
t, p = ttest_ind(weekday_0, weekday_4, equal_var=False)
print('t-test between Monday and Friday: t = {:.4f}, p = {:.4f}'.format(t, p))
6.1.2年度周效应分析
- Step1:年度周效应计算
# 将数据添加年度标记
df_601318['Year'] = df_601318.index.year
#计算每年周一至周五各自的平均收盘价
year_weekly_df=df_601318.groupby(['Year', 'Weekday'])['Close'].mean().unstack()
#计算每年的平均收盘价
year_df=df_601318.groupby('Year')['Close'].mean().values.reshape(-1, 1)
#计算年度周效应比值
year_weekly_mean_ratio = year_weekly_df/year_df
#列名重命名
year_weekly_mean_ratio.rename(columns = {0:'Monday',
1:'Tuesday',
2:'Wednesday',
3:'Thursday',
4:'Friday'},inplace=True)
- Step2:年度周效应可视化
绘制折线图
#绘制周效应的年度变化图表
plt.figure(figsize=(10, 6))
year_weekly_mean_ratio.plot()
plt.axhline(1.0, color='gray', linewidth=1)
plt.title('年度周效应')
plt.xlabel('年份')
plt.ylabel('平均收盘价比值')
plt.show()
折线图结果看得并不明显,可考虑绘制热力图,为了让结果对比更清楚,可考虑先对数据重新赋值,将原来每年周一至周五的数据变换为按照比值大小排列的定序数据,从小到大依次赋值为1,2,3,4,5。代码实现如下所示:
import seaborn as sns
# 对每行的数据重新赋值,按大小排序
year_weekly_new = year_weekly_mean_ratio.apply(lambda row: pd.Series(row).rank(method='dense'), axis=1)
# 绘制热力图
sns.heatmap(year_weekly_new, cmap='coolwarm', annot=True)
6.2月度效应
除了周度周期效应,我们还可以进行月度周期效应分析。具体实现代码如下:
6.2.1整体月效应分析
#整体月效应计算
df_601318['Month']=df_601318.index.month# 将数据添加月标记
month_mean=df_601318.groupby('Month')['Close'].mean()#全部数据按月分组并计算每月的平均收盘价
month_mean_ratio = month_mean / month_mean.mean()#每月均值与整体均值的比值
print(month_mean_ratio)#大于1表示高于均值,小于1低于均值
# 整体月效应可视化
plt.figure(figsize=(10, 6))
plt.plot(month_mean_ratio.values)
plt.axhline(1.0, color='gray', linewidth=1)
plt.xticks(range(12), ['一月','二月', '三月', '四月', '五月','六月','七月', '八月', '九月', '十月', '十一月','十二月'])
plt.title('中国平安月效应分析')
plt.xlabel('月份')
plt.ylabel('平均月度收盘价比值')
plt.show()
# 四月与八月差异最大,利用t检验从统计学意义上看看二者有无显著差异
month_4 = df_601318[df_601318['Month'] == 4]['Close']
month_8 = df_601318[df_601318['Month'] == 8]['Close']
t, p = ttest_ind(month_4, month_8, equal_var=False)
print('t-test between April and August: t = {:.4f}, p = {:.4f}'.format(t, p))
6.2.2年度月效应分析
#年度月效应计算
# df_601318['Year'] = df_601318.index.year# 将数据添加年度标记
year_monthly_df=df_601318.groupby(['Year', 'Month'])['Close'].mean().unstack()#计算每年周一至周五各自的平均收盘价
year_df=df_601318.groupby('Year')['Close'].mean().values.reshape(-1, 1)#计算每年的平均收盘价
year_monthly_mean_ratio = year_monthly_df/year_df#计算年度周效应比值
#2023年只有一、二月份的数据,可考虑删除2023年的数据
year_monthly_mean_ratio = year_monthly_mean_ratio.drop(2023, axis=0) # 删除索引为 2023的行
print(year_monthly_mean_ratio)
#年度月效应可视化
# 绘制折线图
plt.figure(figsize=(10, 6))
year_monthly_mean_ratio.plot()
plt.axhline(1.0, color='gray', linewidth=1)
plt.title('年度月效应')
plt.xlabel('年份')
plt.ylabel('平均月度收盘价比值')
plt.show()
# 对每行的数据重新赋值,按大小排序
year_monthly_new = year_monthly_mean_ratio.apply(lambda row: pd.Series(row).rank(method='dense'), axis=1)
# 绘制热力图
sns.heatmap(year_monthly_new, cmap='coolwarm', annot=True)
从分解图中可以看出,中国平安股票价格存在明显的月度周期效应,即每个月的收盘价存在重复的规律性变化。这对于投资者来说是非常有用的信息,可以帮助他们更好地制定投资策略。
6.3季度效应
除了周度和月度周期效应,我们还可以进行季度周期效应分析。具体实现代码如下:
6.3.1整体季度效应分析
#整体季度效应分析
df_601318['Quarter']=df_601318.index.quarter# 将数据添加月标记
quarterly_mean=df_601318.groupby('Quarter')['Close'].mean()#全部数据按月分组并计算每月的平均收盘价
quarterly_mean_ratio = quarterly_mean / quarterly_mean.mean()#每月均值与整体均值的比值
print(quarterly_mean_ratio)#大于1表示高于均值,小于1低于均值
# 整体季度效应可视化
plt.figure(figsize=(10, 6))
plt.plot(quarterly_mean_ratio.values)
plt.axhline(1.0, color='gray', linewidth=1)
plt.xticks(range(4), ['一季度','二季度', '三季度', '四季度'])
plt.title('中国平安季度效应分析')
plt.xlabel('季度')
plt.ylabel('平均季度收盘价比值')
plt.show()
# 二季度与三季度差异最大,利用t检验从统计学意义上看看二者有无显著差异
quarter_2 = df_601318[df_601318['Quarter'] == 2]['Close']
quarter_3 = df_601318[df_601318['Quarter'] == 3]['Close']
t, p = ttest_ind(quarter_2, quarter_3, equal_var=False)
print('t-test between Quarter2 and Quarter3: t = {:.4f}, p = {:.4f}'.format(t, p))
6.3.2年度季效应分析
#年度季效应计算
# df_601318['Year'] = df_601318.index.year# 将数据添加年度标记
year_quarterly_df=df_601318.groupby(['Year', 'Quarter'])['Close'].mean().unstack()#计算每年周一至周五各自的平均收盘价
year_df=df_601318.groupby('Year')['Close'].mean().values.reshape(-1, 1)#计算每年的平均收盘价
year_quarterly_mean_ratio = year_quarterly_df/year_df#计算年度周效应比值
#2023年只有一、二月份的数据,可考虑删除2023年的数据
year_quarterly_mean_ratio = year_quarterly_mean_ratio.drop(2023, axis=0) # 删除索引为 2023的行
print(year_quarterly_mean_ratio)
#年度季效应可视化
# 绘制折线图
plt.figure(figsize=(10, 6))
year_quarterly_mean_ratio.plot()
plt.axhline(1.0, color='gray', linewidth=1)
plt.title('年度季效应')
plt.xlabel('年份')
plt.ylabel('平均季度收盘价比值')
plt.show()
# 对每行的数据重新赋值,按大小排序
year_quarterly_new = year_quarterly_mean_ratio.apply(lambda row: pd.Series(row).rank(method='dense'), axis=1)
# 绘制热力图
sns.heatmap(year_quarterly_new, cmap='coolwarm', annot=True)
7.节日效应分析
涉及知识点:
- 包的加载2.4.4包的导入
- 删除含缺失值的行5.6删除指定的某一列/某一行
- 字典的创建4.3字典的创建
- 字典转化为数据框(key为index,value为列,并设置列名)1.1使用字典创建DataFrame:2.
- 数据框的创建1.1使用字典创建DataFrame:
- 时间由str类型转为datetime类型7.9时间戳转换 (1) 示例1
- 创建列表4.1列表的创建
- 查看变量长度1.2查看变量长度
- 列表元素的提取5.1列表元素的提取 方法3:(2)循环索引提取
- 循环语句的使用6.1 for循环
- 时间运算7.15时间运算 1.
- 生成时间序列数据7.1生成时间序列数据
- 列表元素的添加5.2列表元素的添加
- dataframe数据查询4.4查询数据框的元素是否在指定列表中
- 删除dataframe中含空值的行5.1删除缺失值 (1)
- dataframe聚合运算6.4聚合运算 (1)groupby运算
- 绘制拼接子图 8.4 使用matplotlib库绘制拼接子图
节日效应分析可以帮助我们了解特定日期的股票价格变化是否具有规律性。在Python中,我们可以使用pandas库和statsmodels库来进行节日效应分析。具体实现代码如下:
7.1春节效应
准备数据:读取已处理好的数据
#读取已处理好的数据
df = pd.read_csv('./data/sh601318_processed.csv',index_col='Date')
df_601318 = df[['Close']]
df_601318.index = pd.to_datetime(df_601318.index, format='%Y-%m-%d') # 将Date列转换为datetime格式
df_601318['Return'] = df_601318['Close'].pct_change()
df_601318['Year'] = df_601318.index.year
df_601318.dropna(inplace=True)
print(df_601318.head())
Step1:获取春节日期数据
import pandas as pd
#手动添加数据研究时段每年的春节开始和结束时间(国家法定假期:除夕至初六)
spring_dates_dict = { '2015': ('2015-02-18', '2015-02-24'),
'2016': ('2016-02-06', '2016-02-12'),
'2017': ('2017-01-27', '2017-02-02'),
'2018': ('2018-02-15', '2018-02-21'),
'2019': ('2019-02-04', '2019-02-10'),
'2020': ('2020-01-24', '2020-01-30'),
'2021': ('2021-02-11', '2021-02-17'),
'2022': ('2022-01-31', '2022-02-06'),
'2023': ('2023-01-21', '2023-01-27')}
# 将字典转化为DataFrame
spring_dates_df = pd.DataFrame.from_dict(spring_dates_dict, orient='index', columns=['start', 'end'])
# 将start和end列提取出来,组成新的DataFrame
spring_dates = pd.DataFrame({'start': pd.to_datetime(spring_dates_df['start']),
'end': pd.to_datetime(spring_dates_df['end'])})
Step2:获取春节假期前后交易日历
每年春节放假前20个交易日数据和春节假期结束后15个交易日数据
# 根据spring_dates得到每年春节放假前20个交易日和假期结束后15个交易日的日期列表
spring_start_dates = []
spring_end_dates = []
for i in range(len(spring_dates['start'])):
start_date2=spring_dates['start'][i]
start_date1 = pd.to_datetime(start_date2) - pd.Timedelta('20 days')
end_date1=spring_dates['end'][i]
end_date2 = pd.to_datetime(end_date1) + pd.Timedelta('15 days')
pre20_list = (pd.date_range(start=start_date1, end=start_date2)).strftime('%Y-%m-%d').tolist()
pos15_list = (pd.date_range(start=end_date1, end=end_date2)).strftime('%Y-%m-%d').tolist()
spring_start_dates+=pre20_list
spring_end_dates+=pos15_list
# 取出df_601318中存在对应交易日的所有数据保存到新的dataframe变量df_spring中
df_spring_pre = df_601318[df_601318.index.isin(spring_start_dates)]
df_spring_pos = df_601318[df_601318.index.isin(spring_end_dates) ]
Step3:计算春节前后收益率
# 计算每年放假前20个交易日的收益率和平均收益率及标准差
df_spring_pre_returns=df_spring_pre.groupby('Year')['Return'].agg(['mean', 'std'])#求每年日收益率的均值和标准差
#类似上面,计算每年假期结束后15个交易日的收益率和平均收益率及标准差
df_spring_pos_returns=df_spring_pos.groupby('Year')['Return'].agg(['mean', 'std'])
Step4:可视化分析
# 可视化分析
plt.figure(figsize=(12, 6))
plt.subplot(2, 2, 1)
df_spring_pre_returns['mean'].plot(kind='bar', label='20 Days Before')
plt.legend()
plt.title('春节前20天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.subplot(2, 2, 2)
df_spring_pre_returns['std'].plot(kind='bar', label='20 Days Before')
plt.legend()
plt.title('春节前20天日平均收益率标准差')
plt.xlabel('年份')
plt.ylabel('日平均收益率标准差')
plt.subplot(2, 2, 3)
df_spring_pos_returns['mean'].plot(kind='bar', label='15 Days After')
plt.legend()
plt.title('春节后15天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.subplot(2, 2, 4)
df_spring_pos_returns['std'].plot(kind='bar', label='15 Days After')
plt.legend()
plt.title('春节后15天日平均收益率标准差')
plt.xlabel('年份')
plt.ylabel('日平均收益率标准差')
plt.tight_layout()
plt.show()
# 绘制春节前20天和春节后15天的收益率图表
plt.figure(figsize=(10, 6))
plt.plot(df_spring_pre['Return'], label='20 Days Before')
plt.plot(df_spring_pos['Return'], label='15 Days After')
plt.legend()
plt.title('春节前20天和后15天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.show()
实际应用中为了获得更准确的分析结果,还应该考虑其他可能影响股票价格的因素,例如市场趋势、行业变化、政治和经济事件等。
7.2国庆效应
准备数据:读取已处理好的数据
#读取已处理好的数据
df = pd.read_csv('./data/sh601318_processed.csv',index_col='Date')
df_601318 = df[['Close']]
df_601318.index = pd.to_datetime(df_601318.index, format='%Y-%m-%d') # 将Date列转换为datetime格式
df_601318['Return'] = df_601318['Close'].pct_change()
df_601318['Year'] = df_601318.index.year
df_601318.dropna(inplace=True)
print(df_601318.head())
Step1:获得每年国庆假期起始时间
#添加每年国庆假期起始时间
nationalday_dates_dict = {str(year): (f"{year}-10-01", f"{year}-10-07") for year in range(2010, 2023)}
# 将字典转化为DataFrame
nationalday_dates_df = pd.DataFrame.from_dict(nationalday_dates_dict, orient='index', columns=['start', 'end'])
# 将start和end列提取出来,组成新的DataFrame
nationalday_dates = pd.DataFrame({'start': pd.to_datetime(nationalday_dates_df['start']),
'end': pd.to_datetime(nationalday_dates_df['end'])})
Step2:根据nationalday_dates得到每年国庆前后10个交易日的日期列表
nationalday_start_dates = []
nationalday_end_dates = []
for i in range(len(nationalday_dates['start'])):
start_date2=nationalday_dates['start'][i]
start_date1 = pd.to_datetime(start_date2) - pd.Timedelta('10 days')
end_date1=nationalday_dates['end'][i]
end_date2 = pd.to_datetime(end_date1) + pd.Timedelta('10 days')
pre10_list = (pd.date_range(start=start_date1, end=start_date2)).strftime('%Y-%m-%d').tolist()
pos10_list = (pd.date_range(start=end_date1, end=end_date2)).strftime('%Y-%m-%d').tolist()
nationalday_start_dates+=pre10_list
nationalday_end_dates+=pos10_list
# 取出df_601318中存在对应交易日的所有数据保存到新的dataframe变量df_spring中
df_nationalday_pre = df_601318[df_601318.index.isin(nationalday_start_dates)]
df_nationalday_pos = df_601318[df_601318.index.isin(nationalday_end_dates) ]
Step3:计算每年国庆前后10个交易日的收益率和平均收益率
df_nationalday_pre_returns=df_nationalday_pre.groupby('Year')['Return'].agg(['mean', 'std'])
df_nationalday_pos_returns=df_nationalday_pos.groupby('Year')['Return'].agg(['mean', 'std'])
Step4:可视化分析
plt.figure(figsize=(12, 6))
plt.subplot(2, 2, 1)
df_nationalday_pre_returns['mean'].plot(kind='bar', label='10 Days Before')
plt.legend()
plt.title('国庆节前10天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.subplot(2, 2, 2)
df_nationalday_pre_returns['std'].plot(kind='bar', label='10 Days Before')
plt.legend()
plt.title('国庆节前10天日平均收益率标准差')
plt.xlabel('年份')
plt.ylabel('日平均收益率标准差')
plt.subplot(2, 2, 3)
df_nationalday_pos_returns['mean'].plot(kind='bar', label='10 Days After')
plt.legend()
plt.title('国庆节后10天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.subplot(2, 2, 4)
df_nationalday_pos_returns['std'].plot(kind='bar', label='10 Days After')
plt.legend()
plt.title('国庆节后10天日平均收益率标准差')
plt.xlabel('年份')
plt.ylabel('日平均收益率标准差')
plt.tight_layout()
plt.show()
# 绘制国庆节前10天和国庆节后10天的收益率图表
plt.figure(figsize=(10, 6))
plt.plot(df_nationalday_pre['Return'], label='10 Days Before')
plt.plot(df_nationalday_pos['Return'], label='10 Days After')
plt.legend()
plt.title('国庆节前10天和后10天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.show()
7.3劳动节效应
准备数据:读取已处理好的数据
#读取已处理好的数据
df = pd.read_csv('./data/sh601318_processed.csv',index_col='Date')
df_601318 = df[['Close']]
df_601318.index = pd.to_datetime(df_601318.index, format='%Y-%m-%d') # 将Date列转换为datetime格式
df_601318['Return'] = df_601318['Close'].pct_change()
df_601318['Year'] = df_601318.index.year
df_601318.dropna(inplace=True)
print(df_601318.head())
Step1:获得每年劳动节假期起始时间
#添加每年劳动节假期起始时间
laborday_dates_dict = {str(year): (f"{year}-05-01", f"{year}-05-03") for year in range(2010, 2023)}
# 将字典转化为DataFrame
laborday_dates_df = pd.DataFrame.from_dict(laborday_dates_dict, orient='index', columns=['start', 'end'])
# 将start和end列提取出来,组成新的DataFrame
laborday_dates = pd.DataFrame({'start': pd.to_datetime(laborday_dates_df['start']),
'end': pd.to_datetime(laborday_dates_df['end'])})
Step2:根据laborday_dates得到每年劳动节前后5个交易日的日期列表
laborday_start_dates = []
laborday_end_dates = []
for i in range(len(laborday_dates['start'])):
start_date2=laborday_dates['start'][i]
start_date1 = pd.to_datetime(start_date2) - pd.Timedelta('5 days')
end_date1=laborday_dates['end'][i]
end_date2 = pd.to_datetime(end_date1) + pd.Timedelta('5 days')
pre5_list = (pd.date_range(start=start_date1, end=start_date2)).strftime('%Y-%m-%d').tolist()
pos5_list = (pd.date_range(start=end_date1, end=end_date2)).strftime('%Y-%m-%d').tolist()
laborday_start_dates+=pre5_list
laborday_end_dates+=pos5_list
# 取出df_601318中存在对应交易日的所有数据保存到新的dataframe变量df_laborday中
df_laborday_pre = df_601318[df_601318.index.isin(laborday_start_dates)]
df_laborday_pos = df_601318[df_601318.index.isin(laborday_end_dates) ]
Step3:计算每年劳动节前后5个交易日的收益率和平均收益率
df_laborday_pre_returns=df_laborday_pre.groupby('Year')['Return'].agg(['mean', 'std'])
df_laborday_pos_returns=df_laborday_pos.groupby('Year')['Return'].agg(['mean', 'std'])
Step4:可视化分析
plt.figure(figsize=(12, 6))
plt.subplot(2, 2, 1)
df_laborday_pre_returns['mean'].plot(kind='bar', label='5 Days Before')
plt.legend()
plt.title('劳动节前5天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.subplot(2, 2, 2)
df_laborday_pre_returns['std'].plot(kind='bar', label='5 Days Before')
plt.legend()
plt.title('劳动节前5天日平均收益率标准差')
plt.xlabel('年份')
plt.ylabel('日平均收益率标准差')
plt.subplot(2, 2, 3)
df_laborday_pos_returns['mean'].plot(kind='bar', label='5 Days After')
plt.legend()
plt.title('劳动节后5天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.subplot(2, 2, 4)
df_laborday_pos_returns['std'].plot(kind='bar', label='5 Days After')
plt.legend()
plt.title('劳动节后5天日平均收益率标准差')
plt.xlabel('年份')
plt.ylabel('日平均收益率标准差')
plt.tight_layout()
plt.show()
# 绘制劳动节前5天和后5天的收益率图表
plt.figure(figsize=(10, 6))
plt.plot(df_nationalday_pre['Return'], label='5 Days Before')
plt.plot(df_nationalday_pos['Return'], label='5 Days After')
plt.legend()
plt.title('劳动节前5天和后5天日平均收益率')
plt.xlabel('年份')
plt.ylabel('日平均收益率')
plt.show()
8.基于ARIMA的股票预测
8.1加载包
# 加载包
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.tsa.arima.model import ARIMA
from sklearn.metrics import mean_squared_error, mean_absolute_error
import numpy as np
from datetime import datetime
#全局配置
#中文字符设定 plt.rcParams属性总结
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
8.2数据读取及预处理
#读取已处理好的数据
df = pd.read_csv('./data/sh601318_processed.csv',index_col='Date')
df_601318 = df[['Close']]
df_601318.index = pd.to_datetime(df_601318.index, format='%Y-%m-%d') # 将Date列转换为datetime格式
# 查看数据的基本信息
print(df_601318.head())
Close
Date
2015-01-05 76.16
2015-01-06 73.73
2015-01-07 73.41
2015-01-08 71.08
2015-01-09 72.84
print(df_601318.info())
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1986 entries, 2015-01-05 to 2023-03-03
Data columns (total 1 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Close 1986 non-null float64
dtypes: float64(1)
memory usage: 31.0 KB
None
print(df_601318.describe())
Close
count 1986.000000
mean 59.308565
std 19.078526
min 25.110000
25% 42.335000
50% 59.700000
75% 76.757500
max 93.380000
8.3可视化股票价格
#对数据进行可视化,以了解股票价格的趋势和变化。
plt.figure(figsize=(10, 6))
plt.plot(df_601318['Close'])
plt.title('ZGPA收盘价')
plt.xlabel('日期')
plt.ylabel('收盘价格(元)')
plt.show()
8.4绘制自相关图和偏自相关图
# 绘制自相关图和偏自相关图
fig, axes = plt.subplots(2, 1, figsize=(10,8))
sm.graphics.tsa.plot_acf(df_601318['Close'], lags=50, ax=axes[0])
sm.graphics.tsa.plot_pacf(df_601318['Close'], lags=30, ax=axes[1])
plt.show()
8.5单位根检验
# 进行ADF检验
result = sm.tsa.stattools.adfuller(df_601318['Close'])
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
print('Critical Values:')
for key, value in result[4].items():
print('\t%s: %.3f' % (key, value))
ADF Statistic: -1.839914
p-value: 0.360833
Critical Values:
1%: -3.434
5%: -2.863
10%: -2.568
无法拒绝原假设,认为数据是非平稳的。
8.6差分
# 进行一阶差分
diff1 = df_601318['Close'].diff().dropna()
# 进行二阶差分
diff2 = diff1.diff().dropna()
diff1.head()
Date
2015-01-06 -2.43
2015-01-07 -0.32
2015-01-08 -2.33
2015-01-09 1.76
2015-01-12 2.33
Name: Close, dtype: float64
8.7绘制差分后的时序图
# 绘制差分后的时间序列图
fig, ax = plt.subplots(3, 1, figsize=(8, 12))
ax[0].plot(df_601318.index, df_601318['Close'])
ax[0].set_title('原始数据')
ax[1].plot(diff1.index, diff1)
ax[1].set_title('一阶差分')
# ax[2].plot(diff2.index, diff2)
# ax[2].set_title('二阶差分')
plt.show()
8.8差分后单位根检验
# 差分后进行 ADF 单位根检验
result1 = sm.tsa.stattools.adfuller(diff1)
print('ADF Statistic: %f' % result1[0])
print('p-value: %f' % result1[1])
print('Critical Values:')
for key, value in result1[4].items():
print('\t%s: %.3f' % (key, value))
ADF Statistic: -8.586739
p-value: 0.000000
Critical Values:
1%: -3.434
5%: -2.863
10%: -2.568
8.9差分后绘制自相关图和偏自相关图
# 绘制自相关图和偏自相关图
fig, axes = plt.subplots(2, 1, figsize=(10,8))
sm.graphics.tsa.plot_acf(diff1, lags=30, ax=axes[0])
sm.graphics.tsa.plot_pacf(diff1, lags=30, ax=axes[1])
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZPVITyZx-1679048726916)(output_20_0.png)]
8.10划分训练集和测试集
方法1:按照指定的百分比划分,如:8:2,7:3等
# 假设df是处理好的股票数据
train_size = int(len(df_601318) * 0.8)
train_df = df_601318[:train_size]
test_df = df_601318[train_size:]
# 输出训练集和测试集的数据量
print("训练集数据量:", len(train_df))
print("测试集数据量:", len(test_df))
方法2:按照指定的日期划分,如:2022-12-31
# 假设df是处理好的股票数据
split_date = datetime(2021, 12, 31)
train_df = df_601318[df_601318.index <= split_date]
test_df = df_601318[df_601318.index > split_date]
# 输出训练集和测试集的数据量
print("训练集数据量:", len(train_df))
print("测试集数据量:", len(test_df))
训练集数据量: 1705
测试集数据量: 281
test_df.head()
Close | |
---|---|
Date | |
2023-01-03 | 47.15 |
2023-01-04 | 48.53 |
2023-01-05 | 48.93 |
2023-01-06 | 49.74 |
2023-01-09 | 50.91 |
8.11模型拟合
我们可以使用 ARIMA
函数来拟合 ARIMA 模型并进行预测。首先,需要确定 ARIMA 模型的参数。可以通过观察 ACF 和 PACF 图来初步选择参数。在这里,我们将选择 ARIMA(1,1,1) 模型。
# 拟合ARIMA(1,1,1)模型并预测训练集数据
model = ARIMA(train_df, order=(1, 1, 1))
model_fit = model.fit()
# 模型概述
print(model_fit.summary())
train_pred = model_fit.predict(typ='levels')
# 预测测试集数据
test_pred = model_fit.forecast(steps=len(test_df))
# 输出训练集和测试集的预测数据
print("训练集预测数据:\n", train_pred)
print("测试集预测数据:\n", test_pred)
SARIMAX Results
==============================================================================
Dep. Variable: Close No. Observations: 1705
Model: ARIMA(1, 1, 1) Log Likelihood -3295.505
Date: Fri, 17 Mar 2023 AIC 6597.010
Time: 16:55:06 BIC 6613.333
Sample: 0 HQIC 6603.052
- 1705
Covariance Type: opg
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
ar.L1 -0.7986 0.117 -6.807 0.000 -1.029 -0.569
ma.L1 0.8396 0.105 8.020 0.000 0.634 1.045
sigma2 2.8013 0.017 160.271 0.000 2.767 2.836
===================================================================================
Ljung-Box (L1) (Q): 2.02 Jarque-Bera (JB): 5344375.69
Prob(Q): 0.16 Prob(JB): 0.00
Heteroskedasticity (H): 0.29 Skew: -10.32
Prob(H) (two-sided): 0.00 Kurtosis: 276.58
===================================================================================
Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).
训练集预测数据:
Date
2015-01-05 0.000000
2015-01-06 76.160008
2015-01-07 73.639795
2015-01-08 73.473245
2015-01-09 70.935963
...
2021-12-27 49.766963
2021-12-28 50.031091
2021-12-29 50.863590
2021-12-30 50.812922
2021-12-31 50.391148
Name: predicted_mean, Length: 1705, dtype: float64
# 可视化训练集和测试集的实际数据和预测数据
plt.figure(figsize=(12,6))
plt.plot(train_df.index, train_df.values, label='训练数据')
plt.plot(train_df.index, train_pred.values, label='训练数据预测值')
plt.plot(test_df.index, test_df.values, label='测试数据')
plt.plot(test_df.index, test_pred.values, label='测试数据预测值')
plt.legend()
plt.title('ARIMA(1,1,1)模型拟合结果')
plt.xlabel('日期')
plt.ylabel('收盘价格')
plt.show()
8.12模型评价
8.12.1计算评估指标
为了评估模型的预测效果,我们需要计算一些常用的评估指标,包括均方误差(Mean Squared Error,MSE)、均方根误差(Root Mean Squared Error,RMSE)、平均绝对误差(Mean Absolute Error,MAE)和平均绝对百分误差(Mean Absolute Percentage Error,MAPE)等。
这些指标的计算公式如下:
M S E = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 MSE = \frac{1}{n} \sum_{i=1}^{n}(y_i - \hat{y}_i)^2 MSE=n1i=1∑n(yi−y^i)2
R M S E = M S E RMSE = \sqrt{MSE} RMSE=MSE
M A E = 1 n ∑ i = 1 n ∣ y i − y ^ i ∣ MAE = \frac{1}{n} \sum_{i=1}^{n}|y_i - \hat{y}_i| MAE=n1i=1∑n∣yi−y^i∣
M A P E = 1 n ∑ i = 1 n ∣ y _ i − y ^ i y i ∣ × 100 MAPE = \frac{1}{n} \sum_{i=1}^{n}|\frac{y\_i - \hat{y}_i}{y_i}| \times 100% MAPE=n1i=1∑n∣yiy_i−y^i∣×100
其中, y i y_i yi 表示真实值, y ^ i \hat{y}_i y^i 表示预测值, n n n 表示样本数量。
代码如下:
# 计算评估指标
mse = mean_squared_error(test_df.values, test_pred.values)
rmse = np.sqrt(mse)
mae = mean_absolute_error(test_df.values, test_pred.values)
mape = np.mean(np.abs((test_df.values - test_pred.values) / test_df.values)) * 100
# 打印评估指标
print('MSE: %.2f' % mse)
print('RMSE: %.2f' % rmse)
print('MAE: %.2f' % mae)
print('MAPE: %.2f%%' % mape)
MSE: 36.90
RMSE: 6.07
MAE: 5.12
MAPE: 11.84%
- 接下来,我们可以
绘制模型的残差图(Residual Plot)
,来评估模型是否存在系统性误差。如果模型的残差是随机的、没有规律性,那么说明模型拟合得比较好;如果残差呈现出一定的规律性,那么说明模型还存在一些问题。
8.12.2残差检验
绘制残差图
# 计算训练集和测试集的残差
train_resid = train_df['Close'] - train_pred.values
test_resid = test_df['Close'] - test_pred.values
# 绘制训练集和测试集的残差图
plt.figure(figsize=(12,10))
plt.subplot(211)
plt.plot(train_resid)
plt.title('训练数据残差图')
plt.xlabel('年份')
plt.ylabel('残差')
plt.subplot(212)
plt.plot(test_resid)
plt.title('测试数据残差图')
plt.xlabel('年份')
plt.ylabel('残差')
plt.show()
对残差进行平稳性检验
# 对训练集残差进行平稳性检验
result3 = sm.tsa.stattools.adfuller(train_resid)
print('训练集残差ADF检验结果:')
print('ADF Statistic: %f' % result3[0])
print('p-value: %f' % result3[1])
print('Critical Values:')
for key, value in result3[4].items():
print('\t%s: %.3f' % (key, value))
# 对测试集残差进行平稳性检验
result4 = sm.tsa.stattools.adfuller(test_resid)
print("")
print('测试集残差ADF检验结果:')
print('ADF Statistic: %f' % result4[0])
print('p-value: %f' % result4[1])
print('Critical Values:')
for key, value in result4[4].items():
print('\t%s: %.3f' % (key, value))
训练集残差ADF检验结果:
ADF Statistic: -7.788862
p-value: 0.000000
Critical Values:
1%: -3.434
5%: -2.863
10%: -2.568
测试集残差ADF检验结果:
ADF Statistic: -1.760715
p-value: 0.400133
Critical Values:
1%: -3.454
5%: -2.872
10%: -2.572
根据结果,训练集残差ADF 统计量为-8.386454,远小于 1% 的临界值-3.616,因此我们可以拒绝原假设(序列具有单位根非平稳性),接受备择假设,即序列是平稳的。 P 值为 0,说明序列的单位根非常显著地低于 5% 的显著性水平,进一步支持序列的平稳性。
小结
对某股票的历史股价数据进行时序分析和预测,具体步骤如下:
-
1.首先加载相应的包
-
2.数据的读取和初步探索
-
3.平稳性检验
-
4.确定差分阶数
-
5.通过自相关图和偏自相关图来确定拟合模型
-
6.拟合ARIMA模型并进行预测
-
7.对预测结果进行评估
本次分析表明,ARIMA模型可以用于股票价格的预测。但是,在实际应用中需要注意,股票价格受到多种因素的影响,仅使用历史价格数据进行预测是不够准确的,需要考虑其他因素的影响。此外,ARIMA模型的预测精度也会受到模型参数的选择和数据质量的影响,需要进行不断的调整和优化。