Python {Article131}

ようこそ「Python」へ...

稼げる仮想通貨を見つけるには〔3〕複数の仮想通貨の累積ログリターンをホバリング機能付きでグラフに表示する【Plotly Express, Hovering】

ここでは8回に分けて稼げる仮想通貨を見つける方法について解説します。 第3回目では、複数の仮想通貨(BTC, ETH, LTC, BCH, XRP,...)の累積ログリターン(Cumulative Log Return)を計算してグラフに表示します。 第2回目の「記事(Article130)」では、Matplotlibを使用してグラフを作成しましたが、今回はPlotly Expressを使用してグラフを作成します。

Plotly Expressを使用すると見た目がキレイなグラフを作成することができますが、 もっとも大きな特徴はマウスをグラフ上にホバリング(Hovering)すると、仮想通貨通貨の「シンボル、日付、累積ログリターン」がポップアップ表示されることです。 この他に、「レンジ・スライダー(RangeSlider)」オプションを有効にするとグラフに表示する仮想通貨の日付をスライドさせて拡大表示するといったことも可能です。 図Fをクリックしてください。 この図には250種類の仮想通貨から累積ログリターンの上位10をPlotly Expressで表示したものです。 マウスを移動してホバリングさせると仮想通貨の「シンボル、累積ログリターン」が表示されます。 ちなみに、第4回目では250の仮想通貨からパフォーマンスのよい仮想通貨上位10と、パフォーマンスの悪い仮想通貨下位10をPlotly Expressを使用してグラデーション付きでグラフに表示する方法を解説します。 なお、累積ログリターンについては第1回目の「記事(Article129)」で詳しく解説しています。

第3回目では、PandasのDataFrameに格納されている仮想通貨のデータをDataFrameの「pivot_table()」メソッドを使用して縦のデータ形式から横のデータ形式に変換しています。 横のデータ形式にすると、仮想通貨ごとのログリターンや累積ログリターンの計算を簡単に行うことができます。 DataFrameに格納されている「累積ログリターン」をPlotly Expressで表示するときは、DataFrameの「melt()」メソッドを使用して横から縦のデータ形式に戻しています。 このように、DataFrameの「pivot_table()」と「melt()」メソッドを使用すると、Matplotlib, Mpfinance, Plotly Expressなどでグラフを表示するときにとても便利です。 ぜひ、この機会に「pivot_table(), melt()」の使い方をマスターしてください。

ここでは米国のYahoo! Financeから仮想通貨のデータをダウンロードします。 仮想通貨のデータは日付で範囲を指定して日次、週次、月次の単位でダウンロードすることができます。 さらに、秒単位や時間単位でダウンロードすることもできます。 Yahoo! Financeからは、仮想通貨の他にGoogle, Amazon, Apple, Microsoft, MetaなどのGAFAMの株価のデータもダウンロードすることができます。

Yahoo! Financeから日次単位で仮想通貨や株価のデータをダウンロードすると、 デイトレードのパフォーマンスを評価することができます。 さらに、月次単位で仮想通貨や株価のデータをダウンロードすると、 積立投資のパフォーマンスを評価するといったことも可能です。

たとえば、あなたが仮想通貨や株の積立投資を5年前からはじめているとすると、現在のリターン(収益率)がどのくらいになっているかが分かります。 さらに、これから積立投資をはじめようとする人にとっては、どの仮想通貨や株に投資した方がもっともパフォーマンスがよいかを調べることもできます。

一般に仮想通貨に投資するときは、次のようなメトリック(投資のパフォーマンスや効果を測定するための指標や数値)を使用して判断します。
  • 日次リターン (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以下であれば買い。
説明文の左側に図の画像が表示されていますが縮小されています。 画像を拡大するにはマウスを画像上に移動してクリックします。 画像が拡大表示されます。拡大された画像を閉じるには右上の[X]をクリックします。 画像の任意の場所をクリックして閉じることもできます。
click image to zoom!
図A PIVOT
click image to zoom!
図B MELT
click image to zoom!
図C Plotly Express[1]
click image to zoom!
図D Plotly Express[2]
click image to zoom!
図E Plotly Express[3]
click image to zoom!
図F Plotly Express[4]
click image to zoom!
図G Plotly Express[5]

複数の仮想通貨(BTC, ETH, LTC,...)の累積ログリターンを計算してホバリング機能付きでグラフに表示する

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

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

    * Article.py:
    # Comparing the Profitability of Multiple Cryptocurrencies Article.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 plotly.offline as offline
    import plotly.express as px
    import plotly.graph_objs as go
    
    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 Time
            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.set_index(['date'], inplace=True)
        return df   
    
    ##############################
    # Main
    ##############################
    
    ### Load the crypto data from yahoo finance
    symbols = ['BTC-USD', 'ETH-USD','LTC-USD']
    # symbols = ['OP-USD']
    # symbols = ['HEX-USD']
    # symbols = ['TWT-USD', 'AAVE-USD', 'RPL-USD'] 
    # symbols = ['MATIC-USD', 'AXS-USD']
    # symbols = ['FMT-USD', 'DOGE-USD', 'TRAC-USD']
    interval = '1d' # 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo
    df_list = []
    for symbol in symbols:
        csv_file = f"data/csv/compare_crypto_20180101({symbol})_{interval}.csv"  # data/csv/compare_crypto_yyyymdd(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(2018,1,1)   # 2018,1,1 or 2020,1,1 or 2023,1,1 or 2023,2,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:
        df = get_data(csv_file)
        df_list.append(df)
    
    raw_df = pd.concat(df_list)
    raw_df.dropna(inplace=True)
    raw_df.isnull().sum() 
    
    
    # %%
    
    
    ### Filter close, symbol columns 
    close_df = raw_df.filter(['close','symbol'])
    close_df.reset_index(inplace=True)
    close_df.groupby('symbol')['date'].agg(['min', 'max', 'count'])
    
    
    # %%
    
    
    ### pivot table
    pivot_df = close_df.pivot_table(index=['date'], columns='symbol', values=['close'])
    pivot_df.dropna(inplace=True)
    pivot_df.isnull().sum() 
    # pivot_df
    
    
    # %%
    
    #### flatten columns multi-index, `date` will become the dataframe index
    
    #                                    col[0]   col[1] 
    # pivot_df.columns.values => array([('close', 'BTC-USD'), ('close', 'ETH-USD'), ('close', 'LTC-USD')])
    pivot_df.columns = [col[1] for col in pivot_df.columns.values]  # ['BTC-USD', 'ETH-USD', 'LTC-USD']
    # pivot_df
    
    
    # %%
    
    ### Calculate log return & cumulative log return
    
    cum_df = pivot_df.copy()
    
    log_col_name_list = []
    cum_col_name_list = []
    
    for symbol in symbols: # BTC-USD, ETH-USD, LTC-USD
        coin = symbol 
        log_col_name = f'log_return_{coin}' # log_return_xxx,...
        log_col_name_list.append(log_col_name)
    
        # Calculate log return
        cum_df[log_col_name] = np.log(cum_df[symbol] / cum_df[symbol].shift(1))
        # cum_df['log_return_xxx'] = np.log(cum_df['xxx'] / cum_df['xxx'].shift(1))
     
        # Calculate cumulative log return
        cum_col_name = f'cum_log_return_{coin}'  # cumulative_log_return_btc
        cum_col_name_list.append(cum_col_name)
    
        cum_df[cum_col_name] = np.exp(cum_df[log_col_name].cumsum()) - 1
        # cum_df['cum_log_return_xxx'] = np.exp(cum_df['log_return_xxx'].cumsum()) - 1
    
        # Preview the resulting dataframe
        print(f"Cumulative Log Return ({symbol}) = {cum_df.iloc[-1][cum_col_name]:.4f}")  
        # print(f"Cumulative Log Return (xxx) = {cum_df.iloc[-1]['cum_log_return_xxx']:.4f}") 
    
    ### Replace np.inf or -np.inf (positive or negative infinity) with np.nan(Not A Number)
    cum_df.replace([np.inf, -np.inf], np.nan, inplace=True)
    cum_df.isnull().sum() 
    
    ### Drop rows if np.nan (Not A Number)
    cum_df.dropna(inplace=True)
    cum_df.isnull().sum() 
    
    
    # %%
    
    ### Transforming the cumulative returns data for plotting.
    
    cum2_df = cum_df.reset_index() 
    
    for i, symbol in enumerate(symbols): # BTC-USD, ETH-USD, LTC-USD
        # replace close price with cumulative log return
        cum2_df[symbol] = cum2_df[cum_col_name_list[i]]
        # cum2_df['BTC-USD'] = cum2_df['cum_log_return_btc']
    
    
    # %%
    
    #### Drop coumuns : log_return_btc,cum_log_return_btc,log_return_eth,cum_log_return_eth,log_return_ltc,cum_log_return_ltc
        
    column_list = cum2_df.columns.to_list()
    # column_list
    # column_list[len(symbols)+1:]
    drop_column_list = column_list[len(symbols)+1:]
    cum3_df = cum2_df.drop(drop_column_list, axis=1)    
    # cum3_df
    
    
    # %%
    cum4_df = cum3_df.melt(id_vars=['date'], var_name='symbol', value_name='cum_log_return')
    cum4_df['cum_log_return_pct'] = cum4_df['cum_log_return'] * 100
    # cum4_df
    
    
    #%%
    
    # Plot line 
    fig = px.line(
        cum4_df, 
        x='date', y='cum_log_return_pct', # log_y=True,
        color='symbol',
        labels={'cum_log_return_pct':'cumulative log returns (%)', }
    )
    
    # Add chart title and axis labels                                  
    fig.update_layout(
        title=f'Performance - Cumulative Log Returns: Interval({interval.upper()})',    
        xaxis_rangeslider_visible=True,    # Set True or False
        xaxis_title='Date' 
    )
    
    fig.show()  
    click image to zoom!
    図1
    図1にはVS Codeの画面が表示されています。 次のステップでは「セル」を選択して「セル」単位でPythonのコードを実行します。
  2. Pythonのライブラリを取り込む

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

    VS Codeから行30-10のセルを選択したら[Ctrl + Enter]で実行します。 ここでは米国のYahoo! Financeからビットコイン(BTC-JPY)、イーサリアム(ETH-JPY)、ライトコイン(LTC-JPY)のデータをダウンロードしています。 ダウンロードするデータは「2018/1/1」から当日「2023/2/16」までの範囲を指定しています。
    click image to zoom!
    図3
    図3にはYahoo! Financeからダウンロードした仮想通貨が格納されているPandasのDataFrameの内容が表示されています。 DataFrameは「open, high, low, close,...」などのカラムから構成されてされています。 インデックスには「date」が設定されています。 ここでは、「date, close, symbol」のカラムを使用します。

    行88-97は仮想通貨のCSVファイルが存在しないときに実行されます。 ファイルが存在しないときは「load_data()」関数を呼び出してYahoo! Financeから仮想通貨のデータをダウンロードします。 「load_data()」からは戻り値としてPandasのDataFrameが返されます。DataFrameには仮想通貨のデータが格納されています。 行97では、DataFrameの「to_csv()」メソッドでDataFrameのデータをCSVファイルに保存しています。 行99では「get_data()」関数を呼び出してCSVファイルに格納されている仮想通貨をPandasのDataFrameに取り込みます。 行100では、list型の変数「df_list」にDataFrameを追加しています。 行102ではPandasの「concat()」メソッドで複数のDataFrameを結合して変数「raw_df」に格納します。 これで「raw_df」には、すべての仮想通貨のデータが格納されることになります。
  4. PandasのDataFrameから「close, symbol」のカラムを抽出する

    行111-113のセルを選択したら[Cntl + Enter」で実行します。 ここではDataFrameから「close, symbol」のカラムのみ抽出して変数「close_df」に格納しています。

    click image to zoom!
    図4
    図4にはDataFrame(close_df)の内容が表示されています。 このDataFrameは「date, close, symbol」のカラムから構成されます。 行112では、DataFrameの「reset_index()」メソッドを実行して「date」のカラムを通常のカラムに戻しています。 行113では、DataFrameの「groupby()」メソッドを実行して「symbol」のカラムでグループ化しています。 さらに「date」のカラムに「agg()」メソッドを適用して「min, max, count」の統計情報を表示させています。 図の下段にはこの実行結果が表示されています。 この実行結果からビットコイン(BTC-USD)のデータは、日付が「2018/1/1」から「2023/2/16」の範囲で1873件格納されていることが分かります。
  5. Pandasのpivot_table()メソッドで仮想通貨の終値(close)を縦形式から横形式に変換する

    行120-122のセルを選択したら[Ctrl + Enter]で実行します。 ここでは、DataFrameのデータ形式を縦から横に変換しています。

    click image to zoom!
    図5
    図5にはDataFrameの形式とデータの内容が表示されています。 DataFrameは「(close, BTC-USD), (close, ETH-USD), (close, LTC-USD)」のカラムから構成されています。 これらのカラムはマルチインデックスになっています。 図の下段にはDataFrameの内容が表示されています。 各カラムには仮想通貨の「終値(close)」が格納されています。
  6. PandasのDataFrameのカラムをフラットにする

    行132のセルを選択したら[Ctrl + Enter]で実行します。 ここではDataFrameのカラムをマルチインデックスからフラットな通常のカラムに変更しています。

    click image to zoom!
    図6-1
    図6-1には行132に記述している「pivot_df.columns.values」の内容を表示しています。 DataFrameの「columns.values」はDataFrameのカラム名を配列(array)として返します。 カラムはマルチインデックス(tuple型)なので変数「col」には「('close', 'BTC-USD')」が格納されます。 「col[0]」は「'close'」、「col[1]」は「'BTC-USD'」を抽出します。 結果的に行132のlist型の領域には「['BTC-USD', 'ETH-USD', 'LTC-USD']」が格納されるので、 DataFrameはフラットなカラム名に変換されます。

    click image to zoom!
    図6-2
    図6-2にはDataFrameの構造と内容が表示されています。 DataFrameは「BTC-USD, ETH-USD, LTC-USD」のカラムから構成されています。 これらのカラムには終値(close)が格納されています。 インデックスには「date」が設定されています。
  7. 仮想通貨のログリターンと累積ログリターンを計算してDataFrameに格納する

    行140-171のセルを選択したら[Ctrl + Enter]で実行します。 ここでは仮想通貨のログリターンと累積ログリターンを計算してDataFrameに格納しています。

    click image to zoom!
    図7-1
    図7-1の上段には行171の「isnull().sum()」の実行結果が表示されています。 すべてのカラムに「0」が表示されているので不正な値(Not A Number)がないことが分かります。

    図の下段にはDataFrameの構造が表示されています。 「log_return_???」にはログリターン、「cum_log_return_???」には累積ログリターンが格納されています。
    click image to zoom!
    図7-2
    図7-2にはDataFrameの内容が表示されています。
  8. 仮想通貨の累積ログリターンをシンボルのカラムに格納する

    行178-182のセルを選択したら[Ctrl + Enter]で実行します。 ここではDataFrameの「BTC-USD, ETH-USD, LTC-USD」のカラムに対応する累積ログリターンを格納しています。

    click image to zoom!
    図8
    図8にはDataFrameの内容が表示されています。 「BTC-USD」のカラムには「cum_log_return_BTC-USD」の値が格納されています。 これで「BTC-USD」の値は、終値から累積ログリターンに置換されました。
  9. DataFrameから不要なカラムを削除する

    行190-194のセルを選択したら[Ctrl + Enter]で実行します。 ここではDataFrameから不要なカラム(log_return_???, cum_log_retun_???)を削除しています。

    click image to zoom!
    図9-1
    図9-1には変数「column_list, drop_column_list」の内容を表示しています。 「column_list」にはDataFrameの全てのカラム名が格納されています。 「drop_column_list」には不要なカラム名が格納されています。 行194のDataFrameの「drop()」メソッドでは、不要なカラムをDataFrameから削除しています。
    click image to zoom!
    図9-2
    図9-2にはDataFrameの内容が表示されています。 DataFrameは「date, BTC-USD, ETH-USD, LTC-USD」のカラムから構成されています。 仮想通貨のシンボルのカラムには累積ログリターンが格納されています。
  10. Pandasのmelt()メソッドで仮想通貨の累積ログリターンを横形式から縦形式に変換する

    行199-200のセルを選択したら[Ctrl + Enter]で実行します。 ここでは、DataFrameの形式を横から縦に戻しています。

    click image to zoom!
    図10
    図10の上段には、DataFrameの「melt()」メソッド実行前のDataFrameが表示されています。 カラム「BTC-USD, ETH-USD, LTC-USD」が横に配列されています。 下段には、「melt()」メソッド実行後のDataFrameが表示されています。 カラム名「BTC-USD, ETH-USD, LTC-USD」が「symbol」のカラムに縦形式に変換されて格納されています。 さらに、「cum_log_return」には累積ログリターンが格納されています。 行200では、累積ログリターンをパーセント(%)に変換して「cum_log_return_ptc」に格納しています。
  11. 複数の仮想通貨の累積ログリターンをグラフに表示する

    行207-221のセルを選択したら[Ctrl + Enter]で実行します。 ここでは、PandasのDataFrameに格納されているデータをPlotly Expressを使用してグラフに表示しています。 「line()」メソッドの引数1には、DataFrame「cum4_df」を指定します。 引数「x」にはDataFrameの日付のカラム「date」を指定します。 引数「y」には累積ログリターン(%)のカラム「cum_log_return_pct」を指定します。

    click image to zoom!
    図11-1
    図11-1では仮想通貨(BTC-USD, ETH-USD, LTC-USD」の累積ログリターンをグラフに表示しています。
    click image to zoom!
    図11-2
    図11-2ではグラフにマウスを移動(ホバリング)させています。 ポップアップが表示されて仮想通貨の「シンボル、日付、累積ログリターン」が表示されています。 この機能は仮想通貨を分析するときとても便利な機能です。 ぜひ、使いこなしてください。
    click image to zoom!
    図11-3
    図11-3ではPloty Expressの「rangeslider」を有効にしたときのグラフが表示されています。 スライダーを左右に移動するとグラフが拡大表示されます。 これも仮想通貨を分析するときとても便利な機能です。
    click image to zoom!
    図11-4
    図11-4では仮想通貨「OP-USD」を追加しています。 マウスでホバリングしているときのポップアップには シンボル「OP-USD」、日付「2023/2/3」、累積ログリターン「340.31%」が表示されています。 他の仮想通貨(BTC, ETH, LTC)と比較すると累積ログリターンがダントツで高いのが分かります。 仮想通貨(BTC, ETH, LTC)を拡大表示(yscale)させるには、行209の「log_y=True」を有効にします。
    click image to zoom!
    図11-5
    図11-5には仮想通貨「TWT-USD, AAVE-USD, RPL-USD」を表示しています。 マウスでホバリングしているときのポップアップには シンボル「TWT-USD」、日付「2022/12/11」、累積ログリターン「263.83%」が表示されています。 これもダントツで高い値になっています。
    click image to zoom!
    図11-6
    図11-6には仮想通貨「DOGE-USD, FMT-USD, TRAC-USD」を表示しています。 マウスでホバリングしているときのポップアップには シンボル「DOGE-USD」、日付「2022/1/14」、累積ログリターン「6.07%」が表示されています。 シリーズの第4回目では250種類の仮想通貨から、 パフォーマンスのベスト10とワースト10の仮想通貨を調べる方法を解説します。