Python {Article129}

ようこそ「Python」へ...

稼げる仮想通貨を見つけるには〔1〕仮想通貨のリターン(収益率)と累積リターンを計算する

ここでは8回に分けて稼げる仮想通貨を見つける方法について解説します。 第1回目では、特定の仮想通貨(BTC, ETH, LTC, BCH, XRP,...)のリターン(Daily Return, Log Return)と累積リターン(Cumulative Return)を日次、時間、分単位で計算します。 なお、リターンについては日次リターン(Daily Return)とログリターン(Log Return)の2種類の計算方法を説明します。

たぶん、あなたは単純に日次リターン(Daily Return)を計算して、それらを加算(SUM)すればよいのではと考えるかもしれません。 しかし、日次リターンは単純に1日あたりの価格変化を示すため、長期的なトレンドを示すことができません。 たとえば、複数の日次リターンが負であったとしても、累積リターンが正である場合があります。 これは、単純な加算ではなく、それらのリターンが積み重なっているからです。

累積リターンは仮想通貨の価格の変化を積算することで、長期的なトレンドを示すことができます。 また、累積リターンを使用することで、投資のパフォーマンスを比較することができます。 たとえば、異なる投資アルゴリズムを採用した場合の累積リターンを比較することで、 どのアルゴリズムがより良いパフォーマンスを発揮しているかを比較することができます。 これらの理由から、日次リターン(Daily Return)を加算(SUM)した値ではなく、累積リターン(Cumulative Return)で分析します。

一般に仮想通貨に投資するときは、次のようなメトリック(投資のパフォーマンスや効果を測定するための指標や数値)を使用して判断します。
  • 日次リターン (Daily Return)
    仮想通貨の投資において1日あたりの収益率を示します。
  • ログリターン (Log Return)
    仮想通貨の投資においての対数収益率を示します。
  • 累積ログリターン (Cumulative Log Return)
    仮想通貨の投資においての累積的な対数収益率を示します。
  • トレーディングボリューム (Trading Volume)
    仮想通貨の取引量を示します。
  • マーケットキャップ (Market Cap)
    仮想通貨の時価総額を示します。
  • 価格波動性 (Price Volatility)
    仮想通貨の価格の変動率を示します。
さらに、実際にトレードするときは、次のようなテクニカルインジケーターを複数組み合わせて売買のタイミングを判断します。
  • Simple Moving Average (SMA)
    SMAが上昇傾向であれば買い。
  • Exponential Moving Average (EMA)
    EMAが上昇傾向であれば買い。
  • Bollinger Bands (BB)
    Bollinger Bandsで帯域が狭まっている場合は価格変動が大きいと見られ、買い/売りタイミングに注意。
  • Relative Strength Index (RSI)
    RSIが70以上であれば売り、30以下であれば買い。
  • Stochastic Oscillator (STO)
    STOが80以上であれば売り、20以下であれば買い。
ここでは仮想通貨に投資するときに使用する「日次リターン、ログリターン、累積リターン」を計算する方法を解説しています。 仮にあなたが仮想通貨の投資家だとした場合、 あなたはどの仮想通貨に投資しますか? 仮想通貨には種類がたくさんあってどの仮想通貨に投資するか悩みますよね。 このような場合には、まずは仮想通貨の累積リターンを計算して、累積リターンが高い仮想通貨に投資するといった考え方もできます。

たとえば、図Gをクリックしてご覧ください。 この図には、仮想通貨(BTC, ETH, LTC)の直近(2023年の2/5~2/12)の累積リターンがグラフに表示されています。 図から分かるように直近では、累積リターンがLTC, BTC, ETHの順になっています。 なので、あなたの投資資金もこの順番に配分するのが理想的です。 なお、複数の仮想通貨の累積リターンを計算してグラフに表示する方法については第2回目の記事で解説します。

説明文の左側に図の画像が表示されていますが縮小されています。 画像を拡大するにはマウスを画像上に移動してクリックします。 画像が拡大表示されます。拡大された画像を閉じるには右上の[X]をクリックします。 画像の任意の場所をクリックして閉じることもできます。
click image to zoom!
図A BTC(Daily)
click image to zoom!
図B BTC(Minutely)
click image to zoom!
図C ETH(Daily)
click image to zoom!
図D LTC(Daily)
click image to zoom!
図E BCH(Daily)
click image to zoom!
図F XRP(Daily)
click image to zoom!
図G BTC,ETH,LTC

ビットコイン(BTC)のリターン(収益率)と累積リターンを計算してみる

  1. まずは、Visual Studio Codeを起動してプログラムファイルを作成する

    Visual Studio Code (VS Code)を起動したら新規ファイル(*.py)を作成して行1-145をコピペします。 ここでは、Jupter NotebookのようにPythonのプログラムをセル単位で実行します。 VS Codeの場合は「#%%」から「#%%」の間がセルになります。 セルを選択したら[Ctrl + Enter」でセルのコードを実行します。 IPythonが起動されて「インタラクティブ」ウィンドウが表示されます。 「インタラクティブ」ウィンドウからはPythonのコードを入力して実行させることができます。 たとえば、「df.info()」を入力して[Shift + Enter」で実行します。

    プログラムのソースコードの解説は音声で行っています。 [音声でコードの解説を聞く!]ボタンをクリックして音声が聞こえたら画面をスクロールして該当する行番号に移動してください。 これで、スマホやパソコンでストレスなくソースコードを理解することができます。



    * Article.py:
    # Daily returns vs Log returns article v00.py
    # %%
    
    ### Import pandas and matplotlib libraries 
    import os
    import math
    import numpy as np
    
    import pandas as pd
    import matplotlib.pyplot as plt 
    import matplotlib.dates as mdates
    
    import datetime as dt
    from datetime import timedelta
    from time import sleep
    import yfinance as yf           
    import warnings
    warnings.simplefilter('ignore')
    plt.style.use('fivethirtyeight')
    pd.set_option('display.max_rows', 10)
    
    
    # %%
    
    ######################################################################################################################################
    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 Date
            if interval in '1m,2m,5m,15m,30m,60m,90m,1h':
                df.rename(columns={'Datetime': 'Date'}, inplace=True)
            else: # interval=1d,5d,1wk,1mo,3mo
                df.rename(columns={'Date': 'Date'}, 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: {csv_file} ")
        df = pd.read_csv(csv_file)       
        # date,open,high,low,close,adj close,volume,symbol
        df['date'] = pd.to_datetime(df['date'])   
        # df['date'] = pd.to_datetime(df['date'], utc=True)          
        df.set_index(['date'], inplace=True)
        return df   
    
    ############################
    # Main
    ############################
    
    ### Load the crypto data from Yahoo Finance
    symbol = 'BTC-USD'  # 'BTC-USD', 'ETH-USD', 'LTC-USD','BCH-USD','XRP-USD'
    interval = '1d'     # 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo 
    
    csv_file = f"data/csv/log_return({symbol})_{interval}.csv"  # data/csv/log_return(BTC_USD)_1d.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)
    # end of if not isFile:
    row_df = get_data(csv_file)
    
    
    # %%
    
    ### Filter close column 
    df = row_df.filter(['close'])
    
    
    # %%
    
    ### Method1: Daily Returns
    
    # Calculate daily return
    df['daily_return'] = df['close'].pct_change(1)
    
    # Calculate cumulative return
    df['cumulative_return'] = (1 + df['daily_return']).cumprod() - 1
    
    # Preview the resulting dataframe
    print(f"{symbol} Cumulative Return = {df.iloc[-1]['cumulative_return']:.4f}")
    # print(f"{symbol} Cumulative Return = {df.iloc[-1]['cumulative_return']:.2%}")
    
    
    # %%
    
    ### Method2: Log Returns
    
    # Calculate log return
    df['log_return'] = np.log(df['close'] / df['close'].shift(1))
    
    # Calculate cumulative log return
    df['cumulative_log_return'] = np.exp(df['log_return'].cumsum()) - 1
    
    # Preview the resulting dataframe
    print(f"{symbol} Cumulative Log Return = {df.iloc[-1]['cumulative_log_return']:.4f}")
    # print(f"{symbol} Cumulative LOg Return = {df.iloc[-1]['cumulative_log_return']:.2%}")
    
    
    # %%
    ### Plot Cumulative Log Returns
    
    plt.figure(figsize=(10,5))
    
    if interval in '1m,2m,5m,15m,30m,60m,90m,1h':
        plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m/%d'))
        # plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d'))
        plt.gca().xaxis.set_major_locator(mdates.DayLocator())
        plt.gcf().autofmt_xdate()
    
    plt.title(f'Cumulative Log Returns : Interval({interval})', fontsize=18)
    plt.plot(df['cumulative_log_return'], label=symbol)
    plt.xticks(rotation=45)
    plt.legend(loc='best')
    plt.show()
    click image to zoom!
    図1
    図1にはVS Codeの画面が表示されています。 次のステップでは「セル」を選択して「セル」単位でPythonのコードを実行します。
  2. Pythonのライブラリを取り込む

    VS Codeから行5-20のセルをクリックして[Ctrl + Enter]で実行します。 IPythonが起動して「インタラクティブ」ウィンドウに実行結果が表示されます。 ここでは、Python 3.10.9とIPython 8.9.0を使用しています。
    click image to zoom!
    図2
    図2ではPythonの各種ライブラリを取り込んでいます。 行18ではPythonの警告メッセージを抑止しています。 行19ではMatplotlibのデフォルトのスタイルを設定しています。 行20では、PanasのDataFrameを表示するときデータ件数を最大10件に制限しています。
  3. 米国のYahoo! Financeからビットコイン(BTC)のデータを取り込む

    VS Codeから行26-91のセルを選択したら[Ctrl + Enter]で実行します。 ここでは米国のYahoo! Financeからビットコイン(BTC-USD)のデータをダウンロードしています。 ダウンロードするデータは「2014/1/1」から当日「2023/2/12」までの範囲を指定しています。

    click image to zoom!
    図3
    図3では、米国のYahoo! Financeからビットコイン(BTC-USD)のデータをダウンロードしてPandasのDataFrameに格納します。 ここではデータの範囲を「2014/1/1」から当日「2023/2/12」に設定しています。 「symbol」には仮想通貨のシンボルを設定します。ここでは「BTC-USD」を設定しています。 「interval」には、「1d」を設定しているので「日次」単位の価格データがダウンロードされます。

    「interval」には「1m, 2m, 3m, 15m,1h,...」などが指定できます。 「m」は分単位、「h」は時間単位を意味します。 「1m」は1分単位の価格をダウンロードします。 Yahoo! FinanceからダウンロードしたデータはCSVファイルに保存します。 次回からはCSVファイルからデータを取り込みます。
  4. PandasのDataFrameから「close: 終値」のカラムを抽出する

    行97のセルを選択したら[Ctrl + Enter]で実行します。 ここでは、PandasのDataFrameから「close:終値」のカラムのみ抽出して変数「df」に格納しています。 抽出したDataFrameにはインデックス(index)も格納されます。 ここでは「date」をインデックスに設定しています。

    click image to zoom!
    図4
    図4では、PandasのDataFrameから「close:終値」のカラムのみ抽出しています。 変数「df」には「close」とインデックスとして「date」が格納されます。
  5. DataFrameのpct_change()メソッドで日次リターン(Daily Return)を計算する

    行105-111のセルを選択したら[Ctrl + Enter]で実行します。 ここではビットコインの日次のリターン(Daily Return)と累積リターン(Cumulative Return)を計算しています。 日次のリターンは、DataFrameのpct_change()メソッドで計算しています。 累積リターンは、DataFrameの累積積「cumprod()」メソッドで計算しています。 print()ではDataFrameの最後の累積リターンを表示しています。

    click image to zoom!
    図5
    図5では日次のリターン(Daily Return)と累積リターン(Cumulative Return)を計算してDataFrameに格納しています。 日次のリターンはDataFrameのpct_change()メソッドで計算しています。 引数に「1」を設定しているので前日の終値と比較して利益率を計算します。

    累積リターンはDataFrameの累積積「cumprod()」メソッドを使用して計算しています。 「cumprod()」は「cumulative product」の略称で累積の積み上げ(product)を意味します。 「cumprod()」メソッドは、DataFrameの各要素に対して累積の積み上げをします。

    たとえば、数列[1, 2, 3, 4]に対して「cumprod()」メソッドを適用すると、[1, 2, 6, 24]が返されます。 余談ですが英語の「product」は、「商品」などの意味で理解していますが数学では「積」の意味になります。
  6. Numpyのlog()メソッドでログリターン(Log Return)を計算する

    行120-126のセルを選択したら[Ctrl + Enter]で実行します。 ここではビットコインのログリターン(Log Return)と累積ログリターン(Cumulative Log Return)を計算しています。 ログリターンは、Numpyのlog()メソッドで計算しています。 累積ログリターンは、DataFrameの累積和「cumsum()」メソッドで計算しています。 print()ではDataFrameの最後の累積ログリターンを表示しています。

    click image to zoom!
    図6
    図6ではNumpyのlog()メソッドを使用してログリターン(Log Return)を計算しています。 累積ログリターン(Cumulative Log Return)の計算はDataFrameの累積和「cumsum()」メソッドを使用して計算しています。 「cumsum()」メソッドは「cumulative sum」の略称で累積の和(sum)を意味します。 「cumsum()」メソッドは、DataFrameの各要素に対して累積の和を求めます。

    たとえば、数列[1, 2, 3, 4]に対して「cumsum()」メソッドを適用すると、[1, 3, 6, 10]が返されます。
  7. ビットコイン(BTC)の累積ログリターンをグラフに表示する

    行133-145のセルを選択したら[Ctrl + Enter]で実行します。 ここではビットコイン(BTC-USD)の累積ログリターン(Cumulative Log Return)をグラフに表示しています。 「interval」に「1d」を指定しているので日次の累積ログリターンがグラフに表示されています。 「interval」には「1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h」などが指定できます。 「m」は分、「h」は時間を意味します。「1m」を選択すると1分間隔の累積ログリターンが表示されます。

    click image to zoom!
    図7-1
    図7-1にはビットコイン(BTC-USD)の日次単位(Daily)の累積ログリターンが表示されています。 ここでは日付を「2014/1/」から「2023/2/12」の範囲で指定しています。
    click image to zoom!
    図7-2
    図7-2には1分単位の累積ログリターンが表示されています。 2023年の2/10~2/13にかけて仮想通貨が暴落しているので累積ログリターンが低くなっています。
    click image to zoom!
    図7-3
    図7-3にはイーサリアム(ETH-USD)の日次単位の累積ログリターンが表示されています。 ここでは日付を「2014/1/」から「2023/2/12」の範囲で指定しています。
    click image to zoom!
    図7-4
    図7-4にはライトコイン(LTC-USD)の日次単位の累積ログリターンが表示されています。 ここでは日付を「2014/1/」から「2023/2/12」の範囲で指定しています。
    click image to zoom!
    図7-5
    図7-5にはビットコイン・キャシュ(BCH-USD)の日次単位の累積ログリターンが表示されています。 ここでは日付を「2014/1/」から「2023/2/12」の範囲で指定しています。
    click image to zoom!
    図7-6
    図7-6にはリップル(XRP-USD)の日次単位の累積ログリターンが表示されています。 ここでは日付を「2014/1/」から「2023/2/12」の範囲で指定しています。