Python {Article122}

ようこそ「Python」へ...

ディープラーニングでビットコイン(BTC)の価格を予測するには〔1〕ビットコインの履歴データを取り込む

ここでは6回に分けて仮想通貨ビットコイン(Bitcoin)の価格を予測する方法を解説します。 このシリーズでは、ディープラーニングのLSTM(Long Short Term Memory)モデルを使用してビットコインの価格を予測します。

第1回目では、Yahoo! Financeからビットコインの過去の価格データを取得する方法を解説します。 ビットコインの価格データは、開始日・終了日を指定して任意の範囲で取得することができます。 また、価格は分単位、時間単位、日付単位、週単位、月単位で取得できます。

ディープラーニングのLSTM(Long Short Term Memory)モデルを使用するには、TensorFlowが必要になります。 TensorFlowは「CPU」版と「GPU」版が用意されています。 それぞれの環境に合わせてどちらかをインストールする必要があります。 TensorFlowの「CPU」版をインストールする手順については 「記事(Article121)【Visual Studio Code編】」で解説しています。 TensorFlowの「GPU」版をインストールする手順については 「記事(Article024)【Anaconda+Jupter Notebook編】」と 「記事(Article025)【Visual Studio Code編】」で解説しています。

この記事では、Pandas、Matplotlibのライブラリも使用するので 「記事(Article001) | 記事(Article002) | 記事(Article003) | 記事(Article004)」 を参照して事前にインストールしておいてください。 Pythonのコードを入力するときにMicrosoftのVisula Studio Codeを使用します。 まだ、インストールしていないときは「記事(Article001)」を参照してインストールしておいてください。

説明文の左側に図の画像が表示されていますが縮小されています。 画像を拡大するにはマウスを画像上に移動してクリックします。 画像が拡大表示されます。拡大された画像を閉じるには右上の[X]をクリックします。 画像の任意の場所をクリックして閉じることもできます。

click image to zoom!
図A df.info()
click image to zoom!
図B df.describe()
click image to zoom!
図C df
click image to zoom!
図D plot()/log_plot()

Yahoo! Financeからビットコインの過去の価格データを取り込む

  1. まずは、Pythonの仮想環境を作成してTensorFlowをインストールする

    ご自分の環境に合わせてTensorFlow[CPU]、またはTensorFlow[GPU]のいずれかをインストールします。 TensorFlow[CPU]のインストール手順は、「記事(Article121)」で解説しています。 TensorFlow[GPU]のインストール手順は、「記事(Article024), 記事(Article025)」で解説しています。

    ここではTensorFlow[CPU]をインストールします。 Pythonのプロジェクトフォルダ「PythonTensorflowCPU」を作成してTensorflow[CPU]をインストールしたら、 Visual Studio Code(VS Code)を起動します。 VS Codeを起動したらPythonの仮想環境「venv」が有効になっていることを確認してください。 VS Codeの「Terminal Window」に「(venv)」が表示されていれば仮想環境は有効になっています。

    VS Codeを起動したら、新規ファイル「Tensorflow CPU.py」を作成して行1-21をコピペして実行します。

    Tensorflow CPU.py:
    # Tensorflow CPU.py
    os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 
    # 0 = all messages are logged (default behavior)
    # 1 = INFO messages are not printed
    # 2 = INFO and WARNING messages are not printed
    # 3 = INFO, WARNING, and ERROR messages are not printed
    
    import sys
    import keras
    import pandas as pd     
    import sklearn as sk    
    import tensorflow as tf
    
    print(f"Keras Version: {keras.__version__}")
    print(f"Tensor Flow Version: {tf.__version__}")
    print()
    print(f"Python {sys.version}")
    print(f"Pandas {pd.__version__}")
    print(f"Scikit-Learn {sk.__version__}")
    gpu = len(tf.config.list_physical_devices('GPU'))>0
    print("GPU is", "available" if gpu else "NOT AVAILABLE (CPU only)")
    click image to zoom!
    図1
    プログラムを実行したらKeras, TensorFlow, Pythonのバージョン番号とGPUの有無を確認します。
  2. Yahoo! Financeからビットコインの過去の価格データを取り込む

    ここでは、Yahoo! Financeからビットコインの過去(2014/1/1から今日まで)の価格データをダウンロードします。 VS Codeから新規ファイル「*.py」を作成したら行1-144をコピペします。 Pythonのソースコードには「#%%」を埋め込んで「Jupter Notebook」のようにセル単位で実行します。 VS Codeでは「#%%...#%%」の間がセルになります。

    行9-36ではPythonの各種ライブラリを取り込んでいます。 行37ではPythonの警告メッセージを抑止しています。 行39ではMatplotlibのデフォルトのスタイルを設定しています。

    行45-76ではユーザー定義関数「load_data()」を定義しています。 この関数ではYahoo! Financeから株価、仮想通貨などの価格データをダウンロードしてPandasのDataFrameに格納して返します。 この関数の引数1には、株価のティッカーまたは仮想通貨のシンボルを指定します。 ビットコインのデータをダウンロードするときは「BTC-USD」を指定します。 引数2, 3にはダウンロードするデータの範囲を指定します。 引数4, 5には範囲の単位と間隔を指定します。 たとえば、「peridod='1d', interval='1m'」と指定したときは、1分間隔のデータがダウンロードされます。 「peridod='1d', interval='1h'」と指定したときは、1時間間隔のデータがダウンロードされます。 「peridod='1d', interval='1d'」と指定したときは、1日間隔のデータがダウンロードされます。

    行79-85ではユーザー定義関数「get_data()」を定義しています。 この関数では、CSVファイルに格納されている株・仮想通貨の価格データをPandasのDataFrameに取り込んで戻り値として返します。

    行88-107ではビットコイン(BTC-USD)の価格データを取り込んでいます。 取り込んだ価格データはCSVファイル「data/csv/*.csv」に保存します。 CSVファイルは「dl_crypto(BTC-USD)_1d.csv」のような名称で保存されます。 すでに価格データがCSVファイルに保存されているときは、CSVファイルから価格データを取り込みます。

    行117-118ではPandasのDataFrameに不正な値がないかチェックしています。

    行132-143ではPandasのDataFrameに格納されている価格「close: 終値」を2種類のグラフ(線グラフ、ラググラフ)に表示します。

    * Article.py:
    # Bitcoin Price Prediction with Deep Learning LSTM Article.py
    #%%
    
    #############################################################
    ### Part 1 : Load / prepare crypto data 
    #############################################################
    
    ### Import the libraires
    import os
    os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
    
    import math
    import numpy as np              # pip install numpy  
    import matplotlib.pyplot as plt # pip install matplotlib
    import matplotlib.dates as mdates
    import pandas as pd             # pip install pandas
    import datetime as dt
    from datetime import timedelta
    from time import sleep
    
    import yfinance as yf           # pip install yfinance
    
    # pip install tensorflow-cpu
    # pip install sklearn
    # pip install scikit-learn
    
    from sklearn.preprocessing import MinMaxScaler
    from keras import models
    from keras.layers import Dense, Dropout, LSTM
    from keras.models import Sequential
    from keras.models import load_model
    
    from sklearn.metrics import mean_squared_error, mean_absolute_error, explained_variance_score, r2_score 
    from sklearn.metrics import mean_poisson_deviance, mean_gamma_deviance, accuracy_score
    
    import warnings
    warnings.simplefilter('ignore')
    
    plt.style.use('fivethirtyeight')
    
    
    # %%
    
    ######################################################################################################################################
    def load_data(symbol: str, start_date: dt.datetime , end_date: dt.datetime, period='1d', interval='1d', prepost=True) -> pd.DataFrame:
        # valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
        # fetch data by interval (including intraday if period < 60 days)
        # valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo    
        try:
            end_date = end_date + timedelta(days=1)
            start_date_str = dt.datetime.strftime(start_date, "%Y-%m-%d")
            end_date_str = dt.datetime.strftime(end_date, "%Y-%m-%d")
            print(f"Loading data for {symbol}: start_date={start_date_str}, end_date={end_date_str}, {period=}, {interval=}")
            df = yf.download(symbol, start=start_date_str, end=end_date_str, period=period, interval=interval, prepost=prepost)
            # Date     Open          High           Low         Close     Adj Close       Volume   Symbol : interval=1d,5d,1wk,1mo,3mo
            # Datetime Open          High           Low         Close     Adj Close       Volume   Symbol : interval=1m,2m,5m,15m,30m,60m,90m,1h
    
            # Add symbol
            df['Symbol'] = symbol 
    
            # Reset index
            df.reset_index(inplace=True) 
    
            # Rename Date or Datetime column name to Time
            if interval in '1m,2m,5m,15m,30m,60m,90m,1h':
                df.rename(columns={'Datetime': 'Time'}, inplace=True)
            else: # interval=1d,5d,1wk,1mo,3mo
                df.rename(columns={'Date': 'Time'}, inplace=True)    
    
            # Convert column names to lower case    
            df.columns = df.columns.str.lower()
    
            return df
        except:
            print('Error loading data for ' + symbol)
            return pd.DataFrame()
    
    ############################################
    def get_data(csv_file: str) -> pd.DataFrame:
        print(f"Loading data for {symbol}: {csv_file} ")
        df = pd.read_csv(csv_file)       
        # time,open,high,low,vlose,ddj close,volume,symbol    
        df['time'] = pd.to_datetime(df['time'])            
        # df.set_index('time', inplace=True) 
        return df    
    
    ### Get the crypto data from Yahoo! Finance
    symbol = 'BTC-USD'
    interval = '1d' 
    csv_file = f"data/csv/dl_crypto({symbol})_{interval}.csv"  # data/csv/dl_crypto(BTC-USD)_{1d|1m}.csv
    isFile = os.path.isfile(csv_file)
    
    if not isFile:    
        if interval in '1m,2m,5m,15m,30m,60m,90m,1h':
            end = dt.datetime.now()          
            start = end - timedelta(days=7)      
        else: # interval=1d,5d,1wk,1mo,3mo  
            start = dt.datetime(2014,1,1)
            end = dt.datetime.now()   
           
        # load_data(symbol: str, start_date: dt.datetime , end_date: dt.datetime, period='1d', interval={'1m'|'1d'}, prepost=True) -> pd.DataFrame:
        df = load_data(symbol, start, end, period='1d', interval=interval)
        if df.shape[0] > 0:    
            df.to_csv(csv_file, index=False)
    else:
        df = get_data(csv_file)
    # end of if not isFile:
    
    # print(df.info())
    # print(df.describe())
    # print(df)
    
    
    #%%
    
    ### Check null values 
    print('Null Values:', df.isnull().values.sum())         
    print('If any NA values:', df.isnull().values.any())    
    
    
    #%%
    
    ### Plot Bitcoin price
    # df.set_index("time").close.plot(figsize=(16,7), title=f"{symbol} Price")
    # plt.show()
    
    
    #%%
    
    ### Plot Line / Lag
    
    plt.figure(figsize=(16,8))
    plt.suptitle('Plots', fontsize=22)
    
    plt.subplot(1,2,1)
    df.set_index("time").close.plot()       # line chart
    plt.title('Line Chart')
    
    plt.subplot(1,2,2)
    pd.plotting.lag_plot(df['close'], lag=1) # daily lag
    plt.title('Daily Lag')
    
    plt.show()
    #%%
    click image to zoom!
    図2-1
    VS Codeからソースコードの行3-41の任意の行をクリックしてセルを選択します。 セルを選択した状態で[Ctrl + Enter]を同時に押してセルを実行します。 「インタラクティブ」ウィンドウが開いて、IPythonが実行されます。

    click image to zoom!
    図2-2
    次に行43-113の任意の行をクリックしてセルを選択したら[Ctrl + Enter]で実行します。 セルが実行されたら「インタラクティブ」ウィンドウの入力エリアに「df.info()」を入力して[Shift + Enter]で実行します。

    click image to zoom!
    図2-3
    「df.info()」が実行されてPandasのDataFrameの構造が表示されました。 DataFrameには3054件のデータが格納されています。 DataFrameは「time, close, symbol,...」等の列(カラム)から構成されています。 ここでは「close:終値」のカラムのみ使用します。

    click image to zoom!
    図2-4
    同様の手順で「インタラクティブ」ウィンドウから「df.describe()」を入力したら「Shift + Enter」で実行します。 DataFrameのカラム別の各種統計情報が表示されます。 ここでは「close」のカラムの「count, mean, std, min, max」に注目してください。 countにはビットコイン(BTC-USD)のデータ件数(3054)が表示されてます。 min, maxにはビットコインの最安値($178.10)と最高値($ 67,566.82)が表示されています。

    click image to zoom!
    図2-5
    最後に「df」を入力したら[Shift + Enter]で実行します。 「インタラクティブ」ウィンドウにはDataFrameの先頭と最後のデータが5件づつ表示されています。 DataFrameにはビットコインの「201/09/17~2023/01/27」のデータが格納されていることが確認できます。
  3. PandasのDataFrameに取り込んだビットコインのデータに不正な値がないかチェックする

    ここでは前出のステップでYahoo! Financeから取り込んだビットコインのデータに不正な値(np.NaN: Not a Number)がないかチェックします。
    click image to zoom!
    図3
    ソースコードの行115-120のセルを選択したら[Ctrl + Enter]で実行します。 「df.isnull().values.sum()」で「0」が表示されているので不正な値(np.NaN)がないことが確認できました。 ちなみに、「df.isnull().values.any()」では、不正な値(np.NaN)があるとき「True」を返します。

  4. PandasのDataFrameに格納されているビットコインの価格をグラフに表示する

    ここではPandasのDataFrameに格納されているビットコインの価格(close)を2種類のグラフ(線グラフ、ラググラフ)に表示します。 ラグフラフは、PandasのDataFrameに格納されているビットコインの価格データをずらして散布図を作成するときに使用します。 たとえば、DataFrameにビットコインの価格が1分間隔で格納されているとします。 この場合、1分単位の価格の散布図を表示するには「lag_plot(, lag=1)」を指定します。 1時間単位の散布図を表示するには「lag_plot(, lag=60)」を指定します。 ここでは、DataFrameにビットコインの価格が日付単位で格納されているので「「lag_plot(, lag=1)」を指定しています。

    click image to zoom!
    図4-1
    「インタラクティブ」ウィンドウに線グラフと日次の散布図(lag=1)が表示されています。
    click image to zoom!
    図4-2
    ここではPandasのDataFrameにビットコインの価格が1分単位で格納されているデータを使用しています。 そして「lag_plot(,lag=1)」、「lag_plot(,lag=60)」で1分単位と1時間単位の散布図を表示しています。