使用Tensorflow作为二进制分类问题预测加密货币价格

介绍

在本教程中,我们将介绍神经网络的原型,该模型将使我们能够使用Keras和Tensorflow作为我们的主要clarvoyance工具来估计未来的加密货币价格(作为二进制分类问题)。

尽管这可能不是解决问题的最佳方法(毕竟,所有投资银行都在开发这种算法上投入了数十亿美元),但如果我们能够超过55%的时间解决问题,那么我们就是有钱了

我们会做什么

  • 使用币安API下载数据
  • 预处理数据
  • 训练我们的模型
  • 特征工程
  • 评估性能最佳的模型

使用币安API下载数据

对于此示例,我们将下载单个调用中可获取的最大数据量。如果您想训练更多更好的东西并在现实世界中使用它(不建议这样做,那么您可能会浪费真钱),我建议您通过多次通话收集更多数据。

import requests
import json
import pandas as pd
import datetime as dt

START_DATE = '2019-01-01'
END_DATE = '2019-10-01'
INTERVAL = '15m'

def parse_date(x):
return str(int(dt.datetime.fromisoformat(x).timestamp()))

def get_bars(symbol, interval):
root_url = 'https://api.binance.com/api/v1/klines'
url = root_url + '?symbol=' + symbol + '&interval=' + interval + '&startTime=' + parse_date(START_DATE) + '&limit=1000'
data = json.loads(requests.get(url).text)
df = pd.DataFrame(data)
df.columns = ['open_time',
'o', 'h', 'l', 'c', 'v',
'close_time', 'qav', 'num_trades',
'taker_base_vol', 'taker_quote_vol', 'ignore']
df.drop(['ignore', 'close_time'], axis=1, inplace=True)
return df

ethusdt = get_bars('ETHUSDT', INTERVAL)
ethusdt.to_csv('./data.csv', index=False)

在这段简单的代码中,我们需要必要的程序包,设置几个参数(我选择了15分钟的时间间隔,但是您可以选择更精细的时间间隔以进行更高频率的交易)并设置几个便利功能,然后将数据保存到csv以供将来重用。这应该是不言自明的,但是如果有什么让您感到困惑的话,请随时发表评论以寻求澄清:)

预处理数据

由于价格加班是顺序数据的一种形式,因此我们将使用LSTM层(长期短期记忆)作为我们网络中的第一层。我们希望提供的数据作为事件的序列,这将预测价格在时间t+n,其中t是当前时间和n定义了多远,我们要预测未来,这样做我们会养活数据的时间窗口w长度。查看代码后,一切将变得更加清晰,让我们开始导入所需的软件包。

import pandas as pd
import numpy as np
import seaborn as sns
import random
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout
from tensorflow.keras.callbacks import TensorBoard
import time
import matplotlib.pyplot as plt

这将导入Pandas,Numpy,我们训练模型所需的所有Tensorflow函数以及其他一些有用的软件包。

接下来,我们要定义一些常量,并从csv加载我们的数据(以防您在其他文件上编写训练代码:

WINDOW = 10 # how many time units we are going to use to evaluate the future value, in our case each time unit is 15 minutes so we are going to look at 15 * 10 = 150 minutes trading data
LOOKAHEAD = 5 # how far ahead we want to estimate if the future prices is going to be higher or lower? In this case is 5 * 15 = 75 minutes in the future
VALIDATION_SAMPLES = 100 # We want to validate our model on data that wasn't used for the training, we are establishing how many data point we are going to use here.

data = pd.read_csv('./data.csv')
data['future_value'] = data['c'].shift(-LOOKAHEAD) # This allows us to define a new column future_value with as the value of c 5 time units in the future
data.drop([
'open_time'
], axis=1, inplace=True) # we don't care about the timestamp for predicting future prices

让我们定义一个函数,该函数使我们可以定义将来的价格是高于还是低于当前收盘价:

def define_output(last, future):
if future > last:
return 1
else:
return 0

如果价格低于或等于当前收盘价,只需将目标设置为0,如果价格高于或高于当前收盘价,则将其设置为1。现在让我们定义一个函数,该函数使我们能够创建需要馈入神经网络的移动时间窗口:

def sequelize(x):
data = x.copy()
buys = []
sells = []
holds = []
data_length = len(data)
for index, row in data.iterrows():
if index <= data_length - WINDOW:
last_index = index + WINDOW -1
rowset = data[index : index + WINDOW]
row_stats = rowset.describe().transpose()
last_close = rowset['c'][last_index]
future_close = rowset['future_value'][last_index]
rowset = 2 * (rowset - row_stats['min']) / (row_stats['max'] - row_stats['min']) - 1
rowset.drop(['future_value'], axis=1, inplace=True)
rowset.fillna(0, inplace=True)
category = define_output(last_close, future_close)
if category == 1:
buys.append([rowset, category])
elif category == 0:
sells.append([rowset, category])
min_len = min(len(sells), len(buys))
results = sells[:min_len] + buys[:min_len]
return results

sequences = sequelize(data)

哦,好,那边有很多东西。让我们一点一点地看一下:

data = x.copy() # let's copy the dataframe, just in case
buys = []
sells = []
holds = []
data_length = len(data)

在这里,我们正在做一些初步的工作,复制数据框以确保我们不覆盖它(例如,如果您使用Jupyter Notebook可能会很烦人),并设置用于买卖的数组,我们将使用它们来平衡数据。

for index, row in data.iterrows():
if index <= data_length - WINDOW:
last_index = index + WINDOW -1
rowset = data[index : index + WINDOW]

如果索引大于我们定义的窗口大小,则在迭代数据中心化的每一行时,我们可以创建数据集的新切片,即窗口大小的大小。在将数据存储到另一个数组中之前,我们需要使用以下代码对其进行规范化:

row_stats = rowset.describe().transpose()
last_close = rowset['c'][last_index]
future_close = rowset['future_value'][last_index] # we'll need to save this separately from the rest of the data
rowset = 2 * (rowset - row_stats['min']) / (row_stats['max'] - row_stats['min']) - 1

而且我们还想从数据中心化删除future_value,并用0替换任何可能的NaN(对于我们的目的而言,理想情况还不够好):

rowset.drop(['future_value'], axis=1, inplace=True)
rowset.fillna(0, inplace=True)

最后,我们要确保我们的买卖平衡,如果其中一种发生的频率比另一种发生的频率高,我们的网络将迅速偏向偏斜,并且无法为我们提供可靠的估计:

if category == 1:
buys.append([rowset, category])
elif category == 0:
sells.append([rowset, category])
# the following 2 lines will ensure that we have an equal amount of buys and sells
min_len = min(len(sells), len(buys))
results = sells[:min_len] + buys[:min_len]
return results

最后,我们对数据运行此功能 sequences = sequelize(data)

随机化我们的数据也是一个好主意,这样我们的模型就不会受到数据集排序的精确顺序的影响,以下代码将对数据集进行随机化,将训练数据集与测试数据集进行拆分,并同时显示这两种数据中的买入与卖出分布数据集。随时重新运行此代码段,以确保更均衡地分配购买和出售:

random.shuffle(sequences)
def split_label_and_data(x):
length = len(x)
data_shape = x[0][0].shape
data = np.zeros(shape=(len(x),data_shape[0],data_shape[1]))
labels = np.zeros(shape=(length,))
for index in range(len(x)):
labels[index] = x[index][1]
data[index] = x[index][0]
return data, labels
x_train, y_train = split_label_and_data(sequences[: -VALIDATION_SAMPLES])
x_test, y_test = split_label_and_data(sequences[-VALIDATION_SAMPLES :])
sns.distplot(y_test)
sns.distplot(y_train)
len(y_train)

在运行了一段代码后,您应该得到类似的东西,两个数据集之间的买卖均分(左对右)。

训练模型

现在我们已经准备好训练模型,但是由于我们尚未探索哪种超参数最适合我们的模型和数据,因此我们将尝试一种稍微复杂的方法。首先让我们定义四个超参数数组:

DROPOUTS = [
0.1,
0.2,
]
HIDDENS = [
32,
64,
128
]
OPTIMIZERS = [
'rmsprop',
'adam'
]
LOSSES = [
'mse',
'binary_crossentropy'
]

然后,我们将遍历每个数组以训练带有超参数组合的模型,以便以后可以使用TensorBoard比较它们:

for DROPOUT in DROPOUTS:
for HIDDEN in HIDDENS:
for OPTIMIZER in OPTIMIZERS:
for LOSS in LOSSES:
train_model(DROPOUT, HIDDEN, OPTIMIZER, LOSS)

现在,我们需要定义train_model将实际创建和训练模型的函数:

def train_model(DROPOUT, HIDDEN, OPTIMIZER, LOSS):
NAME = f"{HIDDEN} - Dropout {DROPOUT} - Optimizer {OPTIMIZER} - Loss {LOSS} - {int(time.time())}"
tensorboard = TensorBoard(log_dir=f"logs/{NAME}", histogram_freq=1)

model = Sequential([
LSTM(HIDDEN, activation='relu', input_shape=x_train[0].shape),
Dropout(DROPOUT),
Dense(HIDDEN, activation='relu'),
Dropout(DROPOUT),
Dense(1, activation='sigmoid')
])
model.compile(
optimizer=OPTIMIZER,
loss=LOSS,
metrics=['accuracy']
)
model.fit(
x_train,
y_train,
epochs=60,
batch_size=64,
verbose=1,
validation_data=(x_test, y_test),
callbacks=[
tensorboard
]
)

目前,这是一个非常简单的模型,其中的LSTM层为第一层,一个Dense中间层,一个大小为1并sigmoid激活的Dense输出层。该层将输出概率(范围从0到1),WINDOWLOOKAHEAD间隔之后,特定大小的序列将跟随较高的收盘价,其中0是较低的收盘价的高概率,而1是较高的收盘价的高概率。收盘价。

我们还添加了一个Tensorboard回调,这将使我们看到每种模型在每个训练周期(EPOCH)的表现

随意在您的终端中运行此代码,然后运行Tensorboard tensorboard --logdir=logs

特征工程

最好的模型在验证数据上的准确性应该高于60%,这已经相当不错了。但是,我们可以通过从现有数据中心化提取更多数据来快速改进模型。从现有特征中提取新特征的过程称为Feature Engineering。特征工程的示例是从数据中提取周末布尔值列,或从坐标对中提取国家/地区。在我们的案例中,我们将技术分析数据添加到我们的OHLC数据中心化。

在笔记本或文件的顶部,添加ta软件包:from ta import *

从csv加载数据后,添加以下行,它将以新列的形式将TA数据追加到我们现有的数据中心化

data = pd.read_csv('./data.csv')
#add the following line
add_all_ta_features(data, "o", "h", "l", "c", "v", fillna=True)
data['future_value'] = data['c'].shift(-LOOKAHEAD)

就是这样,在几行中我们极大地丰富了我们的数据集。现在,我们可以运行模型生成器循环来弄清楚我们的模型如何使用新的数据集,这将花费更长的时间,但值得等待。

丰富,有意义的数据集应确保模型更准确,在上图中,我们可以清楚地看到丰富数据集的性能比简单数据集更好,验证准确性徘徊在80%左右

评估性能最佳的模型。

现在我们有了一些看起来在纸面上表现不错的模型,我们如何评估假设的交易系统中应该使用哪个模型?

这可能是相当主观的,但是我认为一种好的方法是从已知的验证标签分别查看买入和卖出,并绘制相应预测的分布。希望,对于所有购买,我们的模型都可以预测购买,而不是很多出售,反之亦然。

让我们定义一个显示每个模型的K线走势图的函数:

def display_results(NAME, y_test, predictions):
plt.figure()
buys = []
sells = []
for index in range(len(y_test)):
if y_test[index] == 0:
sells.append(predictions[index])
elif y_test[index] == 1:
buys.append(predictions[index])
sns.distplot(buys, bins=10, color='green').set_title(NAME)
sns.distplot(sells, bins=10, color='red')
plt.show()

现在让我们在每次完成模型训练时都调用此函数:

model.fit(
x_train,
y_train,
epochs=60,
batch_size=64,
verbose=0,
validation_data=(x_test, y_test),
callbacks=[
tensorboard
]
)
# after the model.fit call, add the following 2 lines.
predictions = model.predict(x_test)
display_results(NAME, y_test, predictions)

随着不同模型的训练,我们现在应该看到与下图类似的图像,其中买入以绿色绘制(并且我们希望它们在右端,聚集在1值附近),卖出以红色绘制(聚集在左侧为0个值)。这些应有助于我们确定哪种模型可以提供更可靠的未来价格估算。

就是这样,我们现在有一些原型可以使用,它们可以对未来的价格提供合理的估计。作为练习,请尝试以下操作:

  • 如果增加网络的隐藏层数会怎样?
  • 如果您的数据集不平衡会怎样?
  • 如果增加DROPOUT值会怎样?
  • 如果您在新数据上测试最佳模型会怎样?(例如,通过从币安获取不同的时间戳?

如果您有任何问题或建议,请随时在下面评测或建议对本文进行更新:)

原文 Kauri
提示:投资有风险,入市需谨慎,本资讯不作为投资理财建议。请理性投资,切实提高风险防范意识;如有发现的违法犯罪线索,可积极向有关部门举报反映。
你可能还喜欢