使用比特币的历史价格数据对未来价格进行预测

#-------------------------------main.py-----------------------------
# -*- coding: utf-8 -*-

"""

    比特币价格分析
    任务:搭建深度神经网络LSTM,使用比特币的历史价格数据对其未来价格进行预测。

"""

from keras.models import load_model
import os
import pandas as pd
import matplotlib.pyplot as plt

import config
import utils

# 是否加载已经训练好的模型
IS_LOAD_MODEL = True


def main():
    """
        主函数
    """
    # 加载数据集
    all_daily_df = utils.load_data()

    # 对时序数据进行差分操作
    stationary_df = utils.make_stationary_seq(all_daily_df, vis=False)

    # 构建可用于模型的数据
    train_data, test_data = utils.make_data_for_model(stationary_df)

    # 归一化数据
    y_scaler, X_train, y_train, X_test, y_test = utils.scale_data(train_data, test_data)

    if not IS_LOAD_MODEL:
        # 训练LSTM模型
        lstm_model = utils.fit_lstm(X_train, y_train)
    else:
        # 加载LSTM模型
        if os.path.exists(config.model_file):
            lstm_model = load_model(config.model_file)
        else:
            print('{}模型文件不存在'.format(config.model_file))
            return

    # 验证模型
    test_dates = test_data.index.tolist()
    pred_daily_df = pd.DataFrame(columns=['True Value', 'Pred Value'], index=test_dates)
    pred_daily_df['True Value'] = all_daily_df[config.raw_label_col]

    for i, test_date in enumerate(test_dates):
        X = X_test[i].reshape(1, -1)    # 将一天的数据特征转成行向量
        y_pred = utils.forecast_lstm(lstm_model, X)
        # scale反向操作,恢复数据范围
        rescaled_y_pred = y_scaler.inverse_transform(y_pred.reshape(-1, 1))[0, 0]

        # 差分反向操作,恢复数据的值:加上前一天的真实标签
        previous_date = test_date - pd.DateOffset(days=1)
        recoverd_y_pred = rescaled_y_pred + all_daily_df.loc[previous_date][config.raw_label_col]

        # 保存数据
        pred_daily_df.loc[test_date, 'Pred Value'] = recoverd_y_pred
        print('Date={}, 真实值={}, 预测值={}'.format(test_date,
                                               all_daily_df.loc[test_date][config.raw_label_col],
                                               recoverd_y_pred))

    # 保存结果
    pred_daily_df.to_csv(os.path.join(config.output_path, 'pred_daily_df.csv'))
    pred_daily_df.plot()
    plt.savefig(os.path.join(config.output_path, 'pred_daily_df.png'))
    plt.show()


if __name__ == '__main__':
    main()

#----------------------------------utils.py--------------------------------------
# -*- coding: utf-8 -*-

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense

import config


def load_data():
    """
        加载数据集,进行按天重采样,并且将时间设置为索引

        返回:
            - proc_daily_df:处理后的数据集
    """
    data_df = pd.read_csv(config.data_file)
    data_df['date'] = pd.to_datetime(data_df['Timestamp'], unit='s').dt.date
    data_df.set_index('date', inplace=True)
    data_df.index = pd.to_datetime(data_df.index)

    # 确保按时间排序
    data_df.sort_index(inplace=True)

    # 按天重采样
    daily_data_df = data_df.resample('D').mean()
    daily_data_df.dropna(inplace=True)

    # 选取使用的列
    proc_daily_df = daily_data_df[config.stats_cols + [config.raw_label_col]]

    print(proc_daily_df.head())

    return proc_daily_df


def make_stationary_seq(data_df, vis=True):
    """
        对时序数据进行差分,达到平稳

        参数:
            - data_df: 时序数据
            - vis: 是否可视化时序数据

        返回:
            - stationary_df: 平稳化的时序数据
    """
    # 差分操作
    stationary_df = data_df.diff()

    if vis:
        data_df.plot(y=config.raw_label_col, title='Raw Data')
        stationary_df.plot(y=config.raw_label_col, title='Stationary Data')
        plt.show()

    return stationary_df


def make_data_for_model(data_df):
    """
        构造训练集与测试集,并且使用t-1时刻的价格作为一个特征
        参数:
            - data_df: 时序数据
        返回:
            - train_data:   训练数据
            - test_data:    测试数据
    """
    # 使用t-1时刻的价格作为一个特征
    data_df[config.label_col] = data_df[config.raw_label_col].shift(-1)
    data_df.fillna(0, inplace=True)

    use_cols = config.stats_cols + [config.raw_label_col, config.label_col]

    # year_start_pred - 1 前(包括)的时序数据作为训练数据
    train_data = data_df.loc[:str(config.year_start_pred - 1)][use_cols]

    # # year_start_pred 后(包括)的时序数据作为测试数据
    test_data = data_df.loc[str(config.year_start_pred):][use_cols]

    print('训练数据样本个数:{}'.format(train_data.shape[0]))
    print('测试数据样本个数:{}'.format(test_data.shape[0]))

    return train_data, test_data


def scale_data(train_data, test_data):
    """
        对所有数据值做归一化到[-1, 1]

        参数:
            - train_data:   训练数据
            - test_data:    测试数据
        返回:
            - y_scaler:         标签(预测值)的scaler
            - scaled_X_train:   归一化后的训练数据特征
            - scaled_y_train:   归一化后的训练数据标签
            - scaled_X_test:    归一化后的测试数据特征
            - scaled_X_test:    归一化后的测试数据标签
    """
    # 特征上的scaler
    X_scaler = MinMaxScaler(feature_range=(-1, 1))

    # 特征列
    feat_cols = config.stats_cols + [config.raw_label_col]

    # 数据特征
    X_train = train_data[feat_cols].values
    X_test = test_data[feat_cols].values

    # 归一化后的数据特征
    scaled_X_train = X_scaler.fit_transform(X_train)
    scaled_X_test = X_scaler.transform(X_test)

    # 标签的scaler
    y_scaler = MinMaxScaler(feature_range=(-1, 1))

    # 数据标签
    y_train = train_data[config.label_col].values.reshape(-1, 1)
    y_test = test_data[config.label_col].values.reshape(-1, 1)

    # 归一化后的数据标签
    scaled_y_train = y_scaler.fit_transform(y_train)
    scaled_y_test = y_scaler.transform(y_test)

    return y_scaler, scaled_X_train, scaled_y_train, scaled_X_test, scaled_y_test


def fit_lstm(X, y):
    """
        训练LSTM模型

        参数:
            - X:    数据特征
            - y:    数据标签

        返回:
            - model:    训练好的模型
    """
    n_sample = X.shape[0]       # 样本个数
    n_feat_dim = X.shape[1]     # 特征维度

    # shape: (样本个数, time step, 特征维度)
    X = X.reshape(n_sample, config.timestep, n_feat_dim)

    # 构建模型
    model = Sequential()
    model.add(LSTM(config.nodes,
                   batch_input_shape=(config.batch_size, config.timestep, n_feat_dim),
                   stateful=True))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')

    print('开始训练...')
    for i in range(config.nb_epoch):
        print('已迭代{}次(共{}次) '.format(i + 1, config.nb_epoch))
        model.fit(X, y, epochs=1, batch_size=config.batch_size, verbose=0, shuffle=False)
        model.reset_states()

    # 在所有训练样本上运行一次,构建cell状态
    model.predict(X, batch_size=config.batch_size)

    # 保存模型
    model.save(config.model_file)

    return model


def forecast_lstm(model, X):
    """
        预测

        参数:
            - model: 模型
            - X:    数据特征
        返回:
            - y_pred: 预测值
    """
    n_sample = X.shape[0]
    n_feat_dim = X.shape[1]
    X = X.reshape(n_sample, config.timestep, n_feat_dim)
    y_pred = model.predict(X, batch_size=config.batch_size)[0]
    return y_pred


#------------------------config.py---------------------------
# -*- coding: utf-8 -*-

"""
  
"""
import os

# 数据集路径
data_file = './data/coinbaseUSD_1-min_data_2014-12-01_to_2018-01-08.csv'

# 结果保存路径
output_path = './output'
if not os.path.exists(output_path):
    os.makedirs(output_path)

# 数据的统计值列
stats_cols = ['Open', 'High', 'Low', 'Close', 'Volume_(BTC)', 'Volume_(Currency)']

# 原始数据的标签列
raw_label_col = 'Weighted_Price'

# 用于模型训练的标签列
label_col = 'Label_Price'

# 开始预测年份
year_start_pred = 2017

# 模型存放路径
model_file = os.path.join(output_path, 'trained_lstm_model.h5')

# LSTM模型参数
timestep = 1
nodes = 4
batch_size = 1
nb_epoch = 10

 

你可能感兴趣的:(使用比特币的历史价格数据对未来价格进行预测)