https://www.toutiao.com/i6644852565341110791/
利用深度学习来预测股票价格变动(长文,建议收藏)
完整架构概述
在这篇文章中,我将创建一个预测股票价格变动的完整过程。我们将使用生成对抗网络(GAN)与LSTM(一种循环神经网络)作为生成器,使用卷积神经网络CNN作为鉴别器。我们使用LSTM的原因很明显,我们正在尝试预测时间序列数据。为什么我们使用GAN,特别是卷积神经网络(CNN)作为鉴别器呢?这是一个很好的问题:稍后会有特别的部分。
当然,我们将详细介绍每个步骤,但最困难的部分是GAN:成功训练GAN的非常棘手的部分是获得正确的超参数集。出于这个原因,我们将使用贝叶斯优化(还有高斯过程)和深度强化学习(DRL)来决定何时以及如何改变GAN的超参数。在创建强化学习时,我将使用该领域的最新进展,例如Rainbow和PPO。
我们将使用许多不同类型的输入数据。除了股票的历史交易数据和技术指标,我们将使用NLP的最新进展(使用“BERT,对NLP进行迁移学习)来创建情感分析(作为基本面分析的来源) ),用于提取整体趋势方向的傅里叶变换,用于识别其他高级特征的栈式自动编码器,用于查找相关资产的特征投资组合,差分整合移动平均自回归模型(ARIMA))对于股票函数近似,以便捕获尽可能多的关于股票的信息,模式,依赖关系等。我们都知道,数据越多越好。预测股价走势是一项极其复杂的任务,所以我们对股票(从不同的角度)了解得越多,我们的系统就会越好。
为了创建所有神经网络,我们将使用MXNet和它的高级API - Gluon,并在多个GPU上训练它们。
注:尽管我试图深入探讨数学和几乎所有算法和技术背后的机制,但本文并没有明确地解释机器/深度学习或股票市场是如何运作的。其目的是展示我们如何使用不同的技术和算法来准确预测股票价格的变动,并给出每一步使用每种技术的原因和有用性背后的理论基础。
1.简介
准确预测股票市场是一项复杂的任务,因为有数百万种情况会影响它。因此,我们需要能够尽可能多地捕获这些前置条件。我们还需要做出几个重要的假设:1)市场不是100%随机,2)历史重复,3)市场遵循人们的理性行为,4)市场是“ 完美的 ”。
我们将尝试预测高盛(NYSE: GS)的价格走势。为此,我们将使用2010年1月1日至2018年12月31日的每日收盘价(七年数据用于训练,两年数据用于验证)。我们将交替使用“高盛”和“GS”这两个术语。
2.数据
我们需要尽可能多地合并信息(从不同方面和角度描绘股票)。我们将使用每日数据,1585天来训练各种算法(我们有70%的数据)并预测接下来的680天(测试数据),然后我们将把预测结果与测试数据进行比较。每种类型的数据(我们将其称为特征)将在后面的章节中进行更详细的解释,但是,作为一个高层次的概述,我们将使用的特征是:
- 相关资产 - 这些是其他资产(任何类型,不一定是股票,如商品,外汇,指数,甚至固定收益证券)。像高盛这样的大公司显然不会“生活”在一个孤立的世界中 - 它依赖于许多外部因素,并与之相互作用,包括竞争对手,客户,全球经济,地缘政治形势,财政和货币政策,获得资金等。详情将在后面列出。
- 技术指标 - 很多投资者都遵循技术指标。我们将最受欢迎的指标作为独立特征。如 - 7和21天移动平均线,指数移动平均线,momentum,布林线,MACD。
- 一个非常重要的特征,表明股票可能上涨或下跌。基本面分析有两个特征:1)使用10-K和10-Q报告分析公司业绩,分析ROE和P/E等(我们不会使用这个);我们将为高盛(Goldman Sachs)和阅读每日新闻提取总情绪是否对高盛(Goldman Sachs)在那一天是正面的,还是负面的(如得分从0到1)。由于许多投资者会仔细阅读新闻,并根据新闻(当然是部分依据新闻)做出投资决策,因此,如果高盛(Goldman Sachs)今天的消息非常正面,那么该股明天将大幅上涨的可能性在一定程度上是很高的。关键的一点是,我们将在以后对每个特征(包括这个特征)执行特征重要性(意思是它对GS波动的指示性),并决定是否使用它。我们将使用BERT - 谷歌最近公布的NLP方法,用于情感分类股票新闻情绪提取的迁移学习。
- 傅里叶变换 - 除了每日收盘价,我们还将创建傅里叶变换,以概括多个长期和短期趋势。使用这些变换,我们将消除大量噪声(随机游走)并创建真实股票移动的近似值。趋势近似可以帮助LSTM网络更准确地选择其预测趋势。
- 自回归整合移动平均线(ARIMA) - 这是预测时间序列数据未来值的最流行的技术之一(在pre-neural网络时代)。让我们添加它,看看它是否是一个重要的预测特征。
- 栈式自动编码器 - 上述大部分特征(基础分析、技术分析等)都是人们经过几十年的研究发现的。也许有一些隐藏的相关性,人们无法理解,因为有大量的数据点、事件、资产、图表等。通过栈式自动编码器(神经网络的类型),我们可以利用计算机的力量,可能会发现影响股票走势的新类型的特征。即使我们无法理解人类语言中的这些特征,我们也将在GAN中使用它们。
- 期权定价中异常检测的深度无监督学习。我们将再使用一项功能 - 每天我们都会为高盛股票增加90天看涨期权的价格。期权定价本身结合了大量数据。期权合约的价格取决于股票的未来值(分析师也试图预测价格,以便为看涨期权提供最准确的价格)。使用深度无监督机器学习(Self-organized Maps),我们将尝试发现每天定价中的异常情况。异常(例如价格的剧烈变化)可能表明一个事件可能对LSTM了解整个股票模式有用。
接下来,有这么多特征,我们需要执行几个重要步骤:
- 对数据的“质量”进行统计检查。如果我们创建的数据有缺陷,那么无论我们的算法多么复杂,结果都不会是正确的。检查包括确保数据不受异方差、多重共线性或序列相关性的影响。
- 创建特征的重要性。如果一个特征(如另一只股票或一个技术指标)对我们想预测的股票没有解释力,那么我们就没有必要在神经网络的训练中使用它。我们将使用XGBoost(eXtreme Gradient boost),一种boosted 树回归算法。
作为数据准备的最后一步,我们还将使用主成分分析(PCA)创建Eigen投资组合,以减少自动编码器创建的特征的维数。
from utils import *
import time
import numpy as np
from mxnet import nd, autograd, gluon
from mxnet.gluon import nn, rnn
import mxnet as mx
import datetime
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.decomposition import PCA
import math
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import StandardScaler
import xgboost as xgb
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings("ignore")
context = mx.cpu(); model_ctx=mx.cpu()
mx.random.seed(1719)
def parser(x):
return datetime.datetime.strptime(x,'%Y-%m-%d')
dataset_ex_df = pd.read_csv('data/panel_data_close.csv', header=0, parse_dates=[0], date_parser=parser)
dataset_ex_df[['Date', 'GS']].head(3)
print('There are {} number of days in the dataset.'.format(dataset_ex_df.shape[0]))
output >>> There are 2265 number of days in the dataset.
让我们想象一下过去九年的股票。垂直虚线表示训练和测试数据之间的分离。
plt.figure(figsize=(14, 5), dpi=100)
plt.plot(dataset_ex_df['Date'], dataset_ex_df['GS'], label='Goldman Sachs stock')
plt.vlines(datetime.date(2016,4, 20), 0, 270, linestyles='--', colors='gray', label='Train/Test data cut-off')
plt.xlabel('Date')
plt.ylabel('USD')
plt.title('Figure 2: Goldman Sachs stock price')
plt.legend()
plt.show()
num_training_days = int(dataset_ex_df.shape[0]*.7)
print('Number of training days: {}. Number of test days: {}.'.format(num_training_days,
dataset_ex_df.shape[0]-num_training_days))
Number of training days: 1585. Number of test days: 680.
2.1、相关资产
如前所述,我们将使用其他资产作为特征,而不仅仅是GS。
那么,还有哪些资产会影响高盛的股价走势呢?对公司、业务线、竞争环境、依赖关系、供应商和客户类型等的良好理解对于选择正确的相关资产集非常重要:
- 首先是类似于GS的公司。我们将把摩根大通(JPMorgan Chase)和摩根士丹利(Morgan Stanley)等公司加入数据集。
- 作为一家投资银行,高盛(Goldman Sachs)依赖于全球经济。经济不景气或不稳定意味着没有并购或IPO,也可能是有限的自营交易收益。这就是为什么我们将包括全球经济指数。此外,我们将包括LIBOR(美元和英镑计价)利率,因为分析师可能会考虑经济的冲击来设定这些利率以及其他FI证券。
- 每日波动率指数(VIX) - 由于前一点所述的原因。
- 综合指数 - 例如纳斯达克和纽约证券交易所(美国)、FTSE100指数(英国)、Nikkei225指数(日本)、恒生指数和BSE Sensex指数(亚太)。
- 货币 - 全球贸易多次反映货币如何变动,因此我们将使用一篮子货币(如美元兑日元,英镑兑美元等)作为特征。
总的来说,我们在数据集中有72个其他资产 - 每个资产的每日价格。
2.2、技术指标
我们已经讨论了什么是技术指标以及为什么使用它们,现在让我们直接跳到Python代码。我们将只为GS创建技术指标。
def get_technical_indicators(dataset):
# Create 7 and 21 days Moving Average
dataset['ma7'] = dataset['price'].rolling(window=7).mean()
dataset['ma21'] = dataset['price'].rolling(window=21).mean()
# Create MACD
dataset['26ema'] = pd.ewma(dataset['price'], span=26)
dataset['12ema'] = pd.ewma(dataset['price'], span=12)
dataset['MACD'] = (dataset['12ema']-dataset['26ema'])
# Create Bollinger Bands
dataset['20sd'] = pd.stats.moments.rolling_std(dataset['price'],20)
dataset['upper_band'] = dataset['ma21'] + (dataset['20sd']*2)
dataset['lower_band'] = dataset['ma21'] - (dataset['20sd']*2)
# Create Exponential moving average
dataset['ema'] = dataset['price'].ewm(com=0.5).mean()
# Create Momentum
dataset['momentum'] = dataset['price']-1
return dataset
dataset_TI_df = get_technical_indicators(dataset_ex_df[['GS']])
dataset_TI_df.head()
所以我们有每个交易日的技术指标(包括MACD、Bollinger bands等)。我们总共有12项技术指标。
让我们想象一下这些指标的最后400天。
def plot_technical_indicators(dataset, last_days):
plt.figure(figsize=(16, 10), dpi=100)
shape_0 = dataset.shape[0]
xmacd_ = shape_0-last_days
dataset = dataset.iloc[-last_days:, :]
x_ = range(3, dataset.shape[0])
x_ =list(dataset.index)
# Plot first subplot
plt.subplot(2, 1, 1)
plt.plot(dataset['ma7'],label='MA 7', color='g',linestyle='--')
plt.plot(dataset['price'],label='Closing Price', color='b')
plt.plot(dataset['ma21'],label='MA 21', color='r',linestyle='--')
plt.plot(dataset['upper_band'],label='Upper Band', color='c')
plt.plot(dataset['lower_band'],label='Lower Band', color='c')
plt.fill_between(x_, dataset['lower_band'], dataset['upper_band'], al