主页 > imtoken苹果版下载官网怎样 > 基于LSTM的比特币价格预测模型(系列1)

基于LSTM的比特币价格预测模型(系列1)

让预测越来越近!

1

前言

设计并训练一个由输入/训练数据(比特币价格时间序列/60min)驱动的LSTM来预测一小时内的比特币价格,从而在整个测试数据上达到真实价格与预测价格之间的最小值样本均方根误差 (RMSE)。

2

数据准备

基于以太坊 (ETH/USD)、Tezos (XTZ/USD) 和莱特币 (LTC/USD) 三种加密货币的收盘价序列,我们希望预测 BTC/USD 的收盘价,其中可能包括BTC/USD 美元数据。

数据如下:

from ccrypto import getMultipleCrypoSeries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, display_html, HTML
 
ccpairs = ['BTCUSD', 'ETHUSD', 'XTZUSD', 'LTCUSD']
df = getMultipleCrypoSeries(ccpairs, freq='h', exch='Coinbase',
                            start_date='2019-08-09 13:00', end_date='2020-03-13 23:00')
 
display(df)

复制

可视化:

from sklearn.preprocessing import MinMaxScaler
 
scaler = MinMaxScaler(feature_range=(0, 1))
sdf_np = scaler.fit_transform(df)
sdf = pd.DataFrame(sdf_np, columns=df.columns, index=df.index)
 
plt.figure(figsize=(12,6))
plt.grid()
plt.plot(sdf)
plt.legend(sdf.columns)

复制

选择莱特币和以太坊是因为它们与比特币的相关性非常高,无论时间如何。我们可以通过测量滚动窗口大小函数中的平均线性相关性来判断:

blue, orange, red = '#1f77b4', '#ff7f0e', '#d62728'  
plt.figure(figsize=(12,4))
plt.grid()
 
avg_corr1, avg_corr2, avg_corr3 = list(), list(), list()
 
for win in range(3, 72): # hours
    avg_corr1.append(df.ETHUSD.rolling(win).corr(df.BTCUSD) \
                     .replace([np.inf, -np.inf], np.nan).dropna().mean())
    avg_corr2.append(df.XTZUSD.rolling(win).corr(df.BTCUSD) \
                     .replace([np.inf, -np.inf], np.nan).dropna().mean())
    avg_corr3.append(df.LTCUSD.rolling(win).corr(df.BTCUSD) \
                     .replace([np.inf, -np.inf], np.nan).dropna().mean())
    
plt.plot(range(3, 72), avg_corr1, '.-', color=blue, label='ETH vs BTC')
plt.plot(range(3, 72), avg_corr2, '.-', color=orange, label='XTZ vs BTC')
plt.plot(range(3, 72), avg_corr3, '.-', color=red, label='LTC vs BTC')
 
plt.legend()
plt.xlabel('Rolling Window Length [hours]', fontsize=12)
plt.ylabel('Average Correlation', fontsize=12)

复制

3

特征工程

从标签中区分特征。通过标签BTC走势预测,我们将知道我们想要预测的值。例如,比特币 1 小时、2 小时、3 小时价格(标签)或仅 1 小时价格(标签)。

在训练样本中,标签用于训练。为此,我们提供了一系列功能并将相关标签显示到计算机上。例如,如果我们将特征设置为一小时前(T-1) 和两小时前(T-2))三种加密货币的价格,标签是一小时后的比特币价格( t),我们希望计算机学习其他加密货币价格的期望值与比特币的“预期”价格之间的关系。为什么?当价格序列或测试样本之间存在相似性时,比特币价格预测应该更准确。这就是学习的意义所在。如果您徒手触摸煎锅,您将学会不以同样的方式触摸它。

假设我们在上述推理中是正确的。如果是这样,我们可以看看特征和标签准备过程。首先BTC走势预测,让我们准备一个函数,允许在一行中并排显示两个 DataFrame:

def displayS(dfs:list, captions:list, space=5):
    output = ""
    combined = dict(zip(captions, dfs))
    for caption, df in combined.items():
        output += df.style.set_table_attributes("style='display:inline'") \
                  .set_caption(caption)._repr_html_()
        output += space * "\xa0\xa0\xa0"
    display(HTML(output))

复制

接下来,我们将使用 ccrypto 库中的另一个函数,它只接受原始时间序列,允许我们指定应该使用哪个函数作为标签(在我们的例子中是 BTC/USD),以及我们希望使用多少小时使用 Lag 生成一组特征:

label = 'BTCUSD'
 
from ccrypto import get_features_and_labels
 
out_X, _, out_y, _ = get_features_and_labels(df, label,
                                             timesteps=2,
                                             exclude_lagged_label=False,
                                             train_fraction=0  
                                            )
display(df.head())
displayS([out_X.head(), out_y.head()], ['Features:', 'Label:'])

复制

返回结果:

4

训练和测试样本的分离

同样的函数也可以用于将输入数据拆分为训练样本和测试样本。我们所要做的就是决定使用时间序列的哪一部分来训练 LSTM 网络。我们占 95%:

train_fract = 0.95
 
train_X, train_y, test_X, test_y = get_features_and_labels(df, label,
                                                           timesteps=2,
                                                           exclude_lagged_label=False,
                                                           train_fraction=train_fract
                                                          )
 
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)

复制

(4972, 8) (4972, 1) (260, 8) (260, 1)

因此,我们将使用 4972 个观察值乘以 8 个特征来对 260 个新的比特币收盘价进行训练和样本外测试。如果我们能非常准确地预测这260个值,那么研究就是成功了!

data_size = df.shape[0]
train_size = int(data_size * train_fract)
test_size = data_size - train_size
 
plt.figure(figsize=(12,6))
plt.grid()
 
for cn in sdf.columns:
    plt.plot(np.arange(train_size+test_size), sdf[cn], color=grey, \
            label='True Signal ' + cn)
 
plt.plot(np.arange(train_size, train_size+test_size), sdf[label].iloc[train_size:], \
            color=blue, label='Test Signal ' + label)
 
plt.axvspan(train_size, test_size+train_size, facecolor=blue, alpha=0.1)
plt.annotate("Train Sample", xy=(2075, 0.875), fontsize=15, color=grey)
plt.annotate("Test Sample", xy=(4350, 0.075), fontsize=15, color=blue)
plt.xlabel("Data Points")
plt.ylabel("Normalized Time-Series")
plt.legend(loc=3)

复制

5

LSTM 和比特币

我们将设计一个简单的网络架构。公众号将在今天推文的后续部分解释 RNN 和 LSTM 引擎是如何工作的。现在,知道我们的 LSTM 将由 8540 个单元和一个 Dropout 层组成就足够了。输出是一个密集层(一个单元),用于返回预测的比特币价格。

我们将使用 TensorFlow 2.1.x(TF) 来构建 LSTM 网络。

TF 使用基于 keras 的包装器,要求输入数据采用特定格式。首先,我们需要重用 get_features_and_label 函数将训练和测试样本返回为 NumPy 数组而不是 Pandas 的 DataFrame,然后使用 reshape 函数为 TF 准备数据:

train_fract = 0.95
timesteps = 2
train_X, train_y, test_X, test_y = get_features_and_labels(sdf, label,
                                                           timesteps=timesteps,
                                                           train_fraction=train_fract,
                                                           as_np=True,
                                                           exclude_lagged_label=False)
 
train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))
test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))
 
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)

复制

(4972, 1, 8) (4972, 1) (260, 1, 8) (260, 1)

输入数据准备就绪,我们开始设计网络结构:

import tensorflow as tf
 
tf.random.set_seed(7)
 
dropout_fraction = 0.1
units = 8*5
ishape = train_X.shape[1], train_X.shape[2]
 
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.LSTM(units, return_sequences=True, input_shape=ishape))
model.add(tf.keras.layers.Dropout(dropout_fraction))
model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(1)))
 
model.compile(loss='mse', optimizer='adam')
 
print(model.summary())

复制

LSTM网络的训练也可以通过如下拟合函数来进行:

n_epochs = 3000
batch_size = train_X.shape[0]//5
 
history = model.fit(train_X, train_y, epochs=n_epochs, batch_size=batch_size,
                    validation_data=(test_X, test_y), verbose=0, shuffle=False)

复制

epoch 的数量仅仅意味着我们需要训练网络多长时间,其中批量大小是训练中使用的数据点的大小。如果详细设置为 0,model.fit 函数将不会显示拟合的进度。如何verbose=1,我们可以在运行时预览进程:

要可视化损失函数的演变,请使用以下代码:

plt.figure(figsize=(12,2))
plt.grid()
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.ylabel('Loss')
_ = plt.legend()
 
plt.figure(figsize=(12,2))
plt.grid()
plt.ylabel('Loss')
plt.semilogy(history.history['loss'], label='train')
plt.semilogy(history.history['val_loss'], label='test')
_ = plt.legend()

复制

6

测试样本中比特币收盘价的预测

TF 允许使用单行代码对测试样本进行模型预测。为了测试拟合优度(预测的比特币价格与实际价值),我们将使用均方误差回归损失(MSE)来衡量:

yhat = model.predict(test_X)
yorg = sdf[label].iloc[train_X.shape[0]+timesteps:]
 
yhat_f = yhat.flatten()
yorg_f = yorg.values.flatten()
 
from sklearn.metrics import mean_squared_error
rmse = np.sqrt(mean_squared_error(yhat_f, yorg_f))
print('Test MSE: %.5f' % rmse)

复制

测试 MSE:0.01957

这是什么意思?这是我们玩了几个小时后得到的最低误差度量,最终的拟合测试样本如下:

z = np.zeros((test_size-timesteps, 4))
z[:,0] = yhat_f
yhat_inv = scaler.inverse_transform(z)[:,0]
yorg_inv = scaler.inverse_transform(sdf)[:,0]
 
plt.figure(figsize=(12,6))
plt.grid()
plt.plot(df.index[0:train_size], yorg_inv[:train_size], '-', color=grey, \
         label=label+' True Signal (train)')
plt.plot(df.index[train_size+2:], yorg_inv[train_size+2:], '-', color=blue, \
         label=label+' True Signal (test)')
plt.plot(df.index[train_size+2:], yhat_inv, '-', color=red, label=label+ \
         ' Close Price Prediction')
 
plt.title('Test MSE: %.5f' % rmse)
plt.ylabel('BTC/USD', fontsize=12)
plt.xlim([pd.to_datetime('2020-03-01'), df.index[-1]])
plt.ylim([4000, 9500])
_ = plt.legend(loc=3)

复制

查看预测值与实际比特币收盘价的差异:

btc = yorg_inv[train_size+2:]
btc_hat = yhat_inv
 
plt.figure(figsize=(12,4))
plt.grid()
res = btc_hat - btc
plt.plot(df.index[train_size+2:], res)
plt.title('Residuals')
plt.xlim([pd.to_datetime('2020-03-01'), df.index[-1]])
plt.savefig("/Users/pawel/Desktop/resid.png", bbox_inches='tight')

复制

这张照片揭示了很多。首先,我们可以看到 RNN LSTM 网络无法预测由冠状病毒相关的恐惧导致的突然价格下跌。在此之前,它可以更好地处理偏离实际价格的预测,范围从 0 美元到 150 美元,以及从 -50 美元到 250 美元。

我们将在下一篇文章中详细介绍。读者敬请期待!

系列 2 正在进行中...