金融数据分析----code详解版

案例:金融数据分析----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.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获取数据

tushare接口示例:

以获取中国平安(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.数据可视化

涉及知识点:


数据可视化是金融分析的重要手段,可以帮助我们更好地理解数据特征和趋势。在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.基本走势分析

涉及知识点:


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) (dfdf.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.周期效应分析

涉及知识点:


基于上述数据,我们可以进行周期效应分析。在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.节日效应分析

涉及知识点:


节日效应分析可以帮助我们了解特定日期的股票价格变化是否具有规律性。在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-0347.15
2023-01-0448.53
2023-01-0548.93
2023-01-0649.74
2023-01-0950.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=1n(yiy^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=1nyiy^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=1nyiy_iy^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模型的预测精度也会受到模型参数的选择和数据质量的影响,需要进行不断的调整和优化。


–未完待续–

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值