1、介绍
本文将介绍如何分析股票历史数据,并实现快速绘图。同时,我们将尝试计算买入和卖出过程中的收益,并标记出涨跌转折点。最后,实验会使用机器学习方法对数据进行建模,并给出股票未来走势的预测结果。
2、知识点
数据采集方法
数据清洗和预处理
绘制 K 线图
绘制相对变化曲线
短期交易策略
股票长期趋势预测
时间序列建模
3、数据清洗和预处理
接下来的内容中,我们以苹果公司的股票价格数据为例进行分析和预测。首先,通过雅虎财经获取苹果公司历年全部股票数据。当然你也可以自行获取最新数据。
import pandas as pd
# 读取数据
df = pd.read_csv(
"http://labfile.oss.aliyuncs.com/courses/1191/AAPL.csv") # 获取苹果公司股票数据
# 查看数据的末尾10行
df.tail()
由于股票数据是典型的时间序列,这里将 Date 列转换为时间索引,并设置为 DataFrame 的索引列。
# 将Date列的数据类型转换为时间索引
df['Date'] = pd.to_datetime(df['Date'])
# 设置Date列为索引列
df.set_index('Date', inplace=True)
# 查看各列的数据类型
df.info()
可以看到,数据集一共包含6列。其中,Open,High,Low,Close 和 Volume 是股票核心数据,分别代表:开盘价,最高价,最低价,收盘价,以及成交量。而 Adj Close 列代表调整后的价格。关于股票价格为什么存在调整,有兴趣可以搜索相关金融学资料阅读。
实验中,我们一般只会用到前面 5 列核心数据,那么就做一个初步的数据清洗工作。
# 删除 Adj Close 列,只能运行一次
df = df.drop(labels='Adj Close', axis=1)
# 查看末尾10行数据
df.tail()
实际上由于股票数据的特殊性,再加上我们通过可靠的来源获取,数据预处理这个环节并没有太多需要做的事情。当然,这里可以确定以下数据集中是否包含有缺失值,防止文件在转换和存储过程中受到影响。
# 返回缺失值数量
df.isnull().values.sum()
上面返回的数字,即代表数据集中包含缺失值的数量。可以看到,这里给出有 5 个缺失值。那么,我们定位缺失值的位置,看看是哪里的数据出了差错。
df[df.isnull().any(axis = 1)]
可以看到,1981-08-10 这天的数据全部缺失。股票数据中包含缺失值,填充就需要注意了。最好是从其他可信渠道获取可靠数据进行填充,而不是使用数学方法。由于 1981-08-10 已经非常久远,我们这里选择直接删除这行数据。
# 删除缺失行
df = df.dropna(axis=0)
# 返回缺失值数量
df.isnull().values.sum()
接下来,我们可以查看股票价格走势。这里以每天的收盘价格为例,这也是通常绘制股票价格趋势线的首选特征。
from matplotlib import pyplot as plt
%matplotlib inline
# 修改子图风格
plt.style.use('seaborn-whitegrid')
close = df.Close
close.plot(figsize=(16, 9))
plt.show()
4、绘制 K 线图
虽然收盘价格一般用于反映股票的价格变化。但实际上在股票交易中还有另一种更为常用的可视化类型:K 线图。
K 线图,原名蜡烛图,又称阴阳图、棒线、红黑线或蜡烛线,常用于展示股票交易数据。K 线就是指将各种股票每日、每周、每月的开盘价、收盘价、最高价、最低价等涨跌变化状况,用图形的方式表现出来。
如果要自行绘制 K 线图,需要编写的代码量比较大。这里我们使用 `mpl_finance` 提供的 K 线图绘制方法完成。
from mpl_finance import candlestick2_ohlc
# 绘制 2018 年 1 月至 3 月的 K 线图
year_2018_1_3 = df['2018-01':'2018-03']
fig, ax = plt.subplots(figsize=(16, 9))
candlestick2_ohlc(ax, year_2018_1_3.Open, year_2018_1_3.High,
year_2018_1_3.Low, year_2018_1_3.Close, width=.7, alpha=.5)
plt.show()
可以看出,K 线图在反映股价变化趋势方便的确更有优势。我们可以通过红色和黑色,轻松地看出当天股价的涨跌情况。于此同时,红色或黑色的色块越长,即表面当天的涨跌幅度更大。
5、绘制相对变化曲线
K 线图主要是用于反映当天的股价变化情况,如果想要看到当天和前一天的相对变化(涨或跌),就需要通过计算后再进行绘图了。 在计算价格相对变化时,通常会有两种不同的方法。分别是:
但是更多情况下,我们会使用价格自然对数之差来计算:
下面,我们通过计算价格自然对数之差来对 2018 年期间的 AAPL 股票数据相对变化情况进行绘图。
import numpy as np
# 截取2018年的数据
year_2018 = df['2018-01':]
year_2018_close = year_2018.Close
# 计算自然对数之差
log_change = np.log(year_2018_close) - np.log(year_2018_close.shift(1))
fig, ax = plt.subplots(figsize=(16, 9))
ax.plot(log_change, ".-")
# 绘制y = 0的水平线
ax.axhline(y=0, color='red', lw=2)
plt.show()
上图中,红线为基准线红线上部代表当天较前一天股票上涨,而下部对应的则正好相反。
计算相对涨跌天数之间的比例:
(log_change < 0).sum() / len(log_change)
或者是将相对涨跌比例情况绘制成饼状图进行可视化
labels = 'drop', 'match', 'rise' # 下跌,持平,上涨
sizes = [(log_change < 0).sum(), (log_change == 0).sum(), (log_change > 0).sum()]
explode = (0, 0.1, 0.1)
plt.pie(x=sizes, labels=labels, explode=explode)
plt.show()
6、短期交易策略
下面,我们根据该支股票的变化曲线,尝试给出一个短期交易策略。首先,我们使用 Pandas 提供的 rolling 函数计算指定 window 中的股票均价,并绘图。
from mpl_finance import candlestick2_ohlc
# 滑动窗口求平均值
short_rolling = year_2018_close.rolling(window=5).mean() # 滑动窗口值为5
long_rolling = year_2018_close.rolling(window=15).mean() # 滑动窗口值为15
fig, ax = plt.subplots(figsize=(16, 9))
ax.plot(year_2018_close.index, year_2018_close, label='year_2018_close')
ax.plot(short_rolling.index, short_rolling, label='5 days rolling')
ax.plot(long_rolling.index, long_rolling, label='15 days rolling')
ax.set_xlabel('Date')
ax.set_ylabel('Closing price (¥)')
ax.legend(fontsize='large')
plt.show()
可以看出,当 window=15 时的变化曲线明显比 window=5 时平缓,这也就抵消了股价在短时间内的波动。 而长期和短期变化曲线的交点,往往就是我们购入或卖出股票的时间点。如果我们是短期投资者,就可以建立一个交易策略。
- 当短期变化曲线从上方交与长期变化曲线,说明股票短期看跌,则卖出。
- 当短期变化曲线从下方交与长期变化曲线,说明股票长期看涨,则买入。
接下来,我们可以将这些交易节点标记并可视化出来。
fig, ax = plt.subplots(figsize=(16, 9))
short_long = np.sign(short_rolling - long_rolling) # np.sign 为符号函数
buy_sell = np.sign(short_long - short_long.shift(1))
buy_sell.plot(ax=ax)
ax.axhline(y=0, color='red', lw=2)
plt.show()
那么,适合买入时间点:
buy_sell[buy_sell == 1]
适合卖出时间点:
buy_sell[buy_sell == -1]
如果我们选择在 2018-02-15 买入苹果的股票,并在 2018-10-10 进行卖出的话。总共的收益为:
year_2018_close['2018-10-10'] - year_2018_close['2018-02-15']
也就是说,不到 8 个月,每一股就得到了 43.4 美元的收益。看来真是值得买买买。
当然,上面我们是通过历史数据建立的交易策略。在未来的变化过程中,可能并不那么有效。尤其是在股票整体长期下行的时期,这种短期交易策略往往依旧会亏本。所以,股市有风险,投资需谨慎。
7、股票长期趋势预测
上面,我们建立了一个短期交易策略,似乎行之有效。不过,这依赖于这支股票的长期趋势,所以长期趋势可以预测吗?
对于这个问题,我认为是很难预测的。原因有以下几点:
- 你可能预测到价格,但是并不能准确预测到达该价格的时间。
- 股价一定程度依赖于信息,但你基本不可能知道明天的信息。
- 市场交易往往是投机行为,并不理性。
虽然说影响股票价格变化的因素很多,但是不要忘记股票是一种典型的时间序列。所以,我们可以尝试从时间序列的角度去捕捉股票价格长期变化的规律。
Prophet 是一种基于加法模型预测时间序列数据的模块,其适用于按照每年、每周和每天等周期分布的时间序列。Prophet 内建了缺失值和异常者优化能力。所以,接下来我们使用 Prophet 建立加法模型来预测苹果公司股价长期趋势。
为了方便传入数据到 Prophet 模型中,首先需要按照 Prophet 接受数据的默认格式整理数据,时间索引命名为 ds,数值命名为 y。同样,使用收盘价来反映股价的变化。
实际上,太久远的数据并不能对预测未来趋势有太多帮助,这一点应该从直观上有所感觉。所以,我们只选择 2000 年之后的数据用于建立模型。
data = df["2000":] # 选择 2000 年之后的数据
data = data['Close'].reset_index()
data = data.rename(columns={'Date': 'ds', 'Close': 'y'})
data
8、时间序列建模
偏差和方差是机器学习中非常常用的两个指标。过拟合时,方差很大,而欠拟合时,偏差很大。接下来就是建立模型,Prophet 模型沿用了 scikit-learn 中 fit, predict 操作,非常方便。值得注意的是,changepoint_prior_scale 参数用于调节模型对变化的敏感程度。数值越高越敏感,数值越低越不敏感。该数值其实相当于偏方差均衡。
import fbprophet
# 定义模型
model = fbprophet.Prophet(changepoint_prior_scale=0.05,
daily_seasonality=True)
# 训练模型
model.fit(data)
# 生成需预测序列
forecast_df = model.make_future_dataframe(periods=365, freq='D')
# 模型预测
forecast = model.predict(forecast_df)
model.plot(forecast, xlabel='Date', ylabel='Close Price $') # 绘制预测图
plt.title('Close Price of AAPL')
如上图所示,黑点代表实际值,蓝线表示预测值,蓝色半透明区域表示置信区间。我们可以尝试不同的 changepoint_prior_scale 值进行比较:
为了将不同颜色绘制出来,我们添加一个 plot_color= 参数,这里需要改造 fbprophet 默认的 plot 函数。
def plot(m, fcst, ax=None, uncertainty=True, plot_cap=True, xlabel='ds',
ylabel='y', plot_color='#0072B2'):
"""Plot the Prophet forecast.
Parameters
----------
m: Prophet model.
fcst: pd.DataFrame output of m.predict.
ax: Optional matplotlib axes on which to plot.
uncertainty: Optional boolean to plot uncertainty intervals.
plot_cap: Optional boolean indicating if the capacity should be shown
in the figure, if available.
xlabel: Optional label name on X-axis
ylabel: Optional label name on Y-axis
Returns
-------
A matplotlib figure.
"""
if ax is None:
fig = plt.figure(facecolor='w', figsize=(10, 6))
ax = fig.add_subplot(111)
else:
fig = ax.get_figure()
fcst_t = fcst['ds'].dt.to_pydatetime()
ax.plot(m.history['ds'].dt.to_pydatetime(), m.history['y'], 'k.')
ax.plot(fcst_t, fcst['yhat'], ls='-', c=plot_color)
if 'cap' in fcst and plot_cap:
ax.plot(fcst_t, fcst['cap'], ls='--', c='k')
if m.logistic_floor and 'floor' in fcst and plot_cap:
ax.plot(fcst_t, fcst['floor'], ls='--', c='k')
if uncertainty:
ax.fill_between(fcst_t, fcst['yhat_lower'], fcst['yhat_upper'],
color=plot_color, alpha=0.2)
ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
fig.tight_layout()
return fig
接下来,开始绘制不同 changepoint_prior_scale 值下的趋势曲线,你可以看到置信区间范围的变化。
fig, ax = plt.subplots(figsize=(16, 9))
for point, color in zip([0.01, 0.05, 0.1], ['blue', 'red', 'yellow']):
temp_model = fbprophet.Prophet(
changepoint_prior_scale=point, daily_seasonality=True)
temp_model.fit(data)
forecast = temp_model.make_future_dataframe(periods=365*2, freq='D') # 2 年
forecast = temp_model.predict(forecast)
plot(temp_model, forecast, ax=ax, xlabel='Date',
ylabel='Close Price ¥', plot_color=color)
Prophet 提供了一个十分方便的方法帮助我们画出股价变化点(Change Points)。变化点代表时间序列从上升与下降趋势的变化时刻。
from fbprophet.plot import add_changepoints_to_plot
# 绘制预测图
fig = model.plot(forecast)
# 增加变化点
a = add_changepoints_to_plot(fig.gca(), model, forecast)
除此之外,通过 Prophet 还可以查看多种维度(天,周,年)下的季节性变化趋势。接下来,通过 plot_components 绘制出股票数据按年,周,天的季节性波动趋势。
from fbprophet.plot import plot_components
# 绘制多维度变化趋势图
model.plot_components(forecast)
可以看出,以年为单位时,苹果公司的股价在上半年的表现较好,而 9 月到次年 1 月期间,往往是整体下行最明显的时段。所以,如果要入手苹果公司股票,也可以将这一特征纳入考量。后面的周和天的变化趋势,看起来没有多大的参考价值。
9、总结
我们从实际角度提供了使用 Python 对股票数据进行处理及分析的一些思路,并对数据进行了可视化。同时,本文么从基于时间序列加法模型的角度对股票数据进行了预测。Prophet 是一款优秀的时间序列建模分析工具,希望大家多多练习将其掌握。