Otama's Playground

AIで遊んだ結果などをつらつら載せていきます。

ChatGPTで高度なテクニカル分析を実現する方法:具体的手順とコード例

以前以下の記事でテクニカル分析を行ってみましたが,丸投げプロンプトだったので分析に用いられた指標も少なく大味な分析になっていました。

otama-playground.hatenablog.com

ここでもう少し人間の手を介在させて,より詳細な分析が得られるようにしてみたいと思います。

方法

  1. まず、テクニカル分析の指標についてChatGPTに教えてもらう。指標の一覧は下のリンクから。 info.monex.co.jp

     次に挙げるテクニカル分析の指標それぞれに対して情報を整理して表を作成してください。
     必要に応じてWebの情報を参照してください。
     表には指標の名前、概要と使用される状況を必ず含めてください。
     その他有用そうな情報があればそれも含めて構いません。
    
     #トレンド分析:
     * 移動平均線
     * MACD
     * ボリンジャーバンド
     * 一目均衡表
     * DMI
     * パラボリック
     * エンベロープ
    
     # オシレーター分析:
     * RSI
     ...(続く)
    
  2. それぞれを計算するコードをChatGPTに作成させる。 全部は大変なのでリンクを参考に代表的なもののみ使用。 info.monex.co.jp

      添付したcsvから以下の指標を計算するpythonコードを生成してください。
      指標を計算する部分は関数として分離してください。
      # トレンド分析:
      * 移動平均線
      * ボリンジャーバンド
      * 一目均衡表
      * MACD
      * RSI
      * ストキャスティクス
      * RCI
    
  3. 2のコードを手元で実行して値を求める。(自分は後でコードを使いまわしたいので自分で実行する方法にしてますが,面倒な方は3もChatGPTにお願いしてしまってよいと思います)

  4. 3の結果をChatGPTに渡して買いか売りかを判断してもらう。

     テクニカル分析を行いました。
     以下の指標を計算した結果が含まれているため、それぞれについて最新の日付を参照し,強い売り、売 
    り、中立、買い、強い買いの5段階評価とそう評価する理由を表としてまとめてください。表のカラム 
    は,指標、指標の値、評価、理由の4列にしてください。
     * MA25
     * MA75
     * ボリンジャーバンド
     * 一目均衡表
     * MACD
     * RSI
     * ストキャスティクス
     * RCI
    
     最後にまとめた結果を俯瞰し、強い売り、売り、中立、買い、強い買いの5段階評価を行ってくださ 
     い。そう評価した理由も教えてください
    

実行結果

1. テクニカル分析の指標について整理してもらった結果

トレンド分析

指標名 概要 使用される状況
移動平均線 過去の一定期間の価格の平均値をプロットした線。トレンドの方向性を把握するのに使われる。 トレンドの継続や反転を確認するため。
MACD EMA(指数平滑移動平均線)の差を用いた指標。シグナル線と交差することで売買シグナルを生成する。 トレンドの強弱や転換点を把握するため。
ボリンジャーバンド 価格の標準偏差を用いてバンドを描く。バンドの幅が広がると変動が大きく、狭まると変動が小さいことを示す。 市場の過熱感や逆張りのシグナルを確認するため。
一目均衡表 価格と時間の関係を示す日本発の指標。転換線、基準線、先行スパンなどで構成され、抵抗帯(雲)を利用して相場の強弱を判断する。 相場のトレンドや抵抗・支持ラインを把握するため。
DMI 方向性指数(Directional Movement Index)。トレンドの強さを測定する指標で、+DIと-DIの交差で売買シグナルを生成する。 トレンドの強弱を確認するため。
パラボリック SAR(ストップ・アンド・リバース)を用いた指標。価格がSARを上回ると買い、下回ると売りのシグナルを生成する。 トレンドの転換点を確認するため。
エンベロープ 移動平均線から一定の割合だけ離れた上下のバンドを描く。価格がエンベロープを超えると売買シグナルを生成する。 トレンドの継続や反転を確認するため。

オシレーター分析

指標名 概要 使用される状況
RSI 相対力指数。一定期間の価格変動を元に買われ過ぎや売られ過ぎを判断する。 買われ過ぎや売られ過ぎの判断、およびダイバージェンスでトレンドの転換を確認するため。
ストキャスティクス 価格の変動率を測定する指標。%Kと%Dの交差で売買シグナルを生成する。 買われ過ぎや売られ過ぎの判断、およびダイバージェンスでトレンドの転換を確認するため。
サイコロジカルライン 一定期間の中で価格が上昇した日数の割合を示す指標。 市場の過熱感を確認するため。
RCI 順位相関指数。価格の順位と時間の相関を測定する。 買われ過ぎや売られ過ぎの判断、逆張りのシグナルを確認するため。
移動平均線乖離率 価格と移動平均線の乖離を測定する指標。価格が移動平均線からどれだけ離れているかを示す。 買われ過ぎや売られ過ぎの判断、およびトレンドの反転を確認するため。

フォーメーション分析

指標名 概要 使用される状況
RSI 相対力指数。一定期間の価格変動を元に買われ過ぎや売られ過ぎを判断する。 買われ過ぎや売られ過ぎの判断、およびダイバージェンスでトレンドの転換を確認するため。
ストキャスティクス 価格の変動率を測定する指標。%Kと%Dの交差で売買シグナルを生成する。 買われ過ぎや売られ過ぎの判断、およびダイバージェンスでトレンドの転換を確認するため。
サイコロジカルライン 一定期間の中で価格が上昇した日数の割合を示す指標。 市場の過熱感を確認するため。
RCI 順位相関指数。価格の順位と時間の相関を測定する。 買われ過ぎや売られ過ぎの判断、逆張りのシグナルを確認するため。
移動平均線乖離率 価格と移動平均線の乖離を測定する指標。価格が移動平均線からどれだけ離れているかを示す。 買われ過ぎや売られ過ぎの判断、およびトレンドの反転を確認するため。

ローソク足分析

指標名 概要 使用される状況
ローソク足 1本のローソク足で始値、高値、安値、終値の4つの価格情報を示す。 価格の変動パターンを視覚的に把握するため。
複数足分析 複数のローソク足を組み合わせて、トレンドの継続や反転を示唆するパターンを分析する。 トレンドの継続や反転を確認するため。
酒田五法 日本古来のローソク足分析手法。三山(三尊)、三川、三空などのパターンを用いてトレンドの転換を示唆する。 トレンドの転換点を確認するため。

その他の分析

指標名 概要 使用される状況
出来高移動平均線 出来高の移動平均線を用いて、市場のエネルギーやトレンドの強さを確認する。 トレンドの強弱を確認するため。
フィボナッチ フィボナッチ数列に基づく価格のリトレースメントレベルを用いて、支持や抵抗レベルを予測する。 トレンドの継続や反転を確認するため。

ゴールデンクロスとデッドクロス

指標名 概要 使用される状況
ゴールデンクロス 短期移動平均線が長期移動平均線を下から上に突き抜ける現象。これにより、上昇トレンドの開始を示唆するシグナルとして使用される。 上昇トレンドの開始を確認するため。売買のタイミングを測るために使用される。
デッドクロス 短期移動平均線が長期移動平均線を上から下に突き抜ける現象。これにより、下降トレンドの開始を示唆するシグナルとして使用される。 下降トレンドの開始を確認するため。売買のタイミングを測るために使用される。

2. コード生成結果

>生成されたコード

import yfinance as yf
import pandas as pd
import numpy as np

def calculate_moving_average(df, window):
    df[f'MA_{window}'] = df['Close'].rolling(window=window).mean()
    return df

def calculate_bollinger_bands(df, window, num_std_dev):
    df['MA'] = df['Close'].rolling(window=window).mean()
    df['BB_Upper'] = df['MA'] + num_std_dev * df['Close'].rolling(window=window).std()
    df['BB_Lower'] = df['MA'] - num_std_dev * df['Close'].rolling(window=window).std()
    return df

def calculate_ichimoku_cloud(df):
    high_9 = df['High'].rolling(window=9).max()
    low_9 = df['Low'].rolling(window=9).min()
    df['Tenkan-sen'] = (high_9 + low_9) / 2

    high_26 = df['High'].rolling(window=26).max()
    low_26 = df['Low'].rolling(window=26).min()
    df['Kijun-sen'] = (high_26 + low_26) / 2

    df['Senkou_span_A'] = ((df['Tenkan-sen'] + df['Kijun-sen']) / 2).shift(26)
    high_52 = df['High'].rolling(window=52).max()
    low_52 = df['Low'].rolling(window=52).min()
    df['Senkou_span_B'] = ((high_52 + low_52) / 2).shift(26)

    df['Chikou_span'] = df['Close'].shift(-26)
    return df

def calculate_macd(df, short_window, long_window, signal_window):
    df['EMA_short'] = df['Close'].ewm(span=short_window, adjust=False).mean()
    df['EMA_long'] = df['Close'].ewm(span=long_window, adjust=False).mean()
    df['MACD'] = df['EMA_short'] - df['EMA_long']
    df['MACD_signal'] = df['MACD'].ewm(span=signal_window, adjust=False).mean()
    return df

def calculate_rsi(df, window):
    delta = df['Close'].diff()
    gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
    rs = gain / loss
    df['RSI'] = 100 - (100 / (1 + rs))
    return df

def calculate_stochastic_oscillator(df, k_window, d_window):
    df['L14'] = df['Low'].rolling(window=k_window).min()
    df['H14'] = df['High'].rolling(window=k_window).max()
    df['%K'] = (df['Close'] - df['L14']) * 100 / (df['H14'] - df['L14'])
    df['%D'] = df['%K'].rolling(window=d_window).mean()
    return df

def calculate_rci(df, window):
    def compute_rci(series):
        n = len(series)
        rank_diff = series.rank() - pd.Series(range(1, n + 1))
        rci = 1 - 6 * (rank_diff ** 2).sum() / (n * (n**2 - 1))
        return rci * 100

    df['RCI'] = df['Close'].rolling(window=window).apply(compute_rci, raw=False)
    return df

def main():
    # Fetch stock data using yfinance
    ticker = 'AAPL'
    df = yf.download(ticker, start='2020-01-01', end='2023-01-01')

    # Calculate indicators
    df = calculate_moving_average(df, window=20)
    df = calculate_bollinger_bands(df, window=20, num_std_dev=2)
    df = calculate_ichimoku_cloud(df)
    df = calculate_macd(df, short_window=12, long_window=26, signal_window=9)
    df = calculate_rsi(df, window=14)
    df = calculate_stochastic_oscillator(df, k_window=14, d_window=3)
    df = calculate_rci(df, window=9)

    # Save the result to a new CSV file
    df.to_csv('/path/to/your/outputfile.csv')

if __name__ == '__main__':
    main()

3. pythonコードの実行

若干修正を加える

  • 移動平均のレンジを25と75(MA25, MA75)
  • データ取得部分を微修正

>最終的なコード

import datetime

import pandas as pd
import numpy as np
import yfinance as yf

def calculate_moving_average(df, window):
    df[f'MA_{window}'] = df['Close'].rolling(window=window).mean()
    return df

def calculate_bollinger_bands(df, window, num_std_dev):
    df['MA'] = df['Close'].rolling(window=window).mean()
    df['BB_Upper'] = df['MA'] + num_std_dev * df['Close'].rolling(window=window).std()
    df['BB_Lower'] = df['MA'] - num_std_dev * df['Close'].rolling(window=window).std()
    return df

def calculate_ichimoku_cloud(df):
    high_9 = df['High'].rolling(window=9).max()
    low_9 = df['Low'].rolling(window=9).min()
    df['Tenkan-sen'] = (high_9 + low_9) / 2

    high_26 = df['High'].rolling(window=26).max()
    low_26 = df['Low'].rolling(window=26).min()
    df['Kijun-sen'] = (high_26 + low_26) / 2

    df['Senkou_span_A'] = ((df['Tenkan-sen'] + df['Kijun-sen']) / 2).shift(26)
    high_52 = df['High'].rolling(window=52).max()
    low_52 = df['Low'].rolling(window=52).min()
    df['Senkou_span_B'] = ((high_52 + low_52) / 2).shift(26)

    df['Chikou_span'] = df['Close'].shift(-26)
    return df

def calculate_macd(df, short_window, long_window, signal_window):
    df['EMA_short'] = df['Close'].ewm(span=short_window, adjust=False).mean()
    df['EMA_long'] = df['Close'].ewm(span=long_window, adjust=False).mean()
    df['MACD'] = df['EMA_short'] - df['EMA_long']
    df['MACD_signal'] = df['MACD'].ewm(span=signal_window, adjust=False).mean()
    return df

def calculate_rsi(df, window):
    delta = df['Close'].diff()
    gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
    rs = gain / loss
    df['RSI'] = 100 - (100 / (1 + rs))
    return df

def calculate_stochastic_oscillator(df, k_window, d_window):
    df['L14'] = df['Low'].rolling(window=k_window).min()
    df['H14'] = df['High'].rolling(window=k_window).max()
    df['%K'] = (df['Close'] - df['L14']) * 100 / (df['H14'] - df['L14'])
    df['%D'] = df['%K'].rolling(window=d_window).mean()
    return df

def calculate_rci(df, window):
    def compute_rci(series):
        n = len(series)
        rank_diff = series.rank() - pd.Series(range(1, n + 1))
        rci = 1 - 6 * (rank_diff ** 2).sum() / (n * (n**2 - 1))
        return rci * 100

    df['RCI'] = df['Close'].rolling(window=window).apply(compute_rci, raw=False)
    return df

def main():
    # Fetch stock data using yfinance
    ticker = 'TM'
    start_date = datetime.date.today() - datetime.timedelta(days=365)
    end_date = datetime.date.today()
    df = yf.download(ticker, start=start_date, end=end_date)

    # Calculate indicators
    df = calculate_moving_average(df, window=25)
    df = calculate_moving_average(df, window=75)
    df = calculate_bollinger_bands(df, window=20, num_std_dev=2)
    df = calculate_ichimoku_cloud(df)
    df = calculate_macd(df, short_window=12, long_window=26, signal_window=9)
    df = calculate_rsi(df, window=14)
    df = calculate_stochastic_oscillator(df, k_window=14, d_window=3)
    df = calculate_rci(df, window=9)

    # Save the result to a new CSV file
    df.to_csv('./output.csv')

if __name__ == '__main__':
    main()

コードの実行

実行するとoutput.csvが生成されます。 全ての指標がデータフレーム内に入り乱れており何が何やら状態ですが、ChatGPTなら理解してくれると信じてそのまま進めます。

4. ChatGPTの評価結果

3で得られたCSVをChatGPTに渡して評価してもらいます。

評価基準

指標 強い買い 買い 中立 売り 強い売り
MA25 短期移動平均線が長期移動平均線を上回るクロスが確認された場合 短期移動平均線が長期移動平均線より上 短期移動平均線と長期移動平均線が交差している 短期移動平均線が長期移動平均線より下 短期移動平均線が長期移動平均線を下回るクロスが確認された場合
ボリンジャーバンド 価格がボリンジャーバンドの下限に触れた場合 価格がボリンジャーバンドの下限付近にある場合 価格がボリンジャーバンドの中間帯にある場合 価格がボリンジャーバンドの上限付近にある場合 価格がボリンジャーバンドの上限に触れた場合
一目均衡表 価格が雲の上にあり、転換線が基準線を上回るクロスが確認された場合 価格が雲の上にある場合 価格が雲の中にある場合 価格が雲の下にある場合 価格が雲の下にあり、転換線が基準線を下回るクロスが確認された場合
MACD MACDがシグナルラインを上回るクロスが確認された場合 MACDがシグナルラインより上 MACDとシグナルラインが交差している MACDがシグナルラインより下 MACDがシグナルラインを下回るクロスが確認された場合
RSI RSIが30以下 RSIが30から50の間 RSIが50 RSIが50から70の間 RSIが70以上
ストキャスティクス %Kが20以下 %Kが20から50の間 %Kが50 %Kが50から80の間 %Kが80以上
RCI RCIが-80以下 RCIが-80から-20の間 RCIが-20から20の間 RCIが20から80の間 RCIが80以上

評価結果

評価にChatGPTが使用したであろうコード

>評価コード

# Extract the latest data row
latest_data = data.iloc[-1]

# Extracting the necessary indicators
indicators = {
    'MA25': latest_data['MA_20'],
    'MA75': latest_data['MA'],
    'BB_Upper': latest_data['BB_Upper'],
    'BB_Lower': latest_data['BB_Lower'],
    'Tenkan-sen': latest_data['Tenkan-sen'],
    'Kijun-sen': latest_data['Kijun-sen'],
    'Senkou_span_A': latest_data['Senkou_span_A'],
    'Senkou_span_B': latest_data['Senkou_span_B'],
    'Chikou_span': latest_data['Chikou_span'],
    'MACD': latest_data['MACD'],
    'MACD_signal': latest_data['MACD_signal'],
    'RSI': latest_data['RSI'],
    '%K': latest_data['%K'],
    '%D': latest_data['%D'],
    'RCI': latest_data['RCI']
}

# Initialize a dictionary to store the evaluation results
evaluation = []

# Function to evaluate the indicators
def evaluate_indicators(latest_data):
    close_price = latest_data['Close']
    # MA25 and MA75
    if latest_data['MA_20'] > latest_data['MA']:
        evaluation.append(['MA25', latest_data['MA_20'], '買い', 'MA25がMA75を上回っているため'])
    elif latest_data['MA_20'] < latest_data['MA']:
        evaluation.append(['MA25', latest_data['MA_20'], '売り', 'MA25がMA75を下回っているため'])
    else:
        evaluation.append(['MA25', latest_data['MA_20'], '中立', 'MA25とMA75が交差しているため'])

    # Bollinger Bands
    if close_price < latest_data['BB_Lower']:
        evaluation.append(['ボリンジャーバンド', close_price, '強い買い', '価格がボリンジャーバンドの下限に触れているため'])
    elif close_price > latest_data['BB_Upper']:
        evaluation.append(['ボリンジャーバンド', close_price, '強い売り', '価格がボリンジャーバンドの上限に触れているため'])
    elif close_price < (latest_data['BB_Lower'] + latest_data['BB_Upper']) / 2:
        evaluation.append(['ボリンジャーバンド', close_price, '買い', '価格がボリンジャーバンドの下限付近にあるため'])
    else:
        evaluation.append(['ボリンジャーバンド', close_price, '売り', '価格がボリンジャーバンドの上限付近にあるため'])

    # Ichimoku Cloud
    if close_price > latest_data['Senkou_span_A'] and close_price > latest_data['Senkou_span_B']:
        if latest_data['Tenkan-sen'] > latest_data['Kijun-sen']:
            evaluation.append(['一目均衡表', close_price, '強い買い', '価格が雲の上にあり、転換線が基準線を上回っているため'])
        else:
            evaluation.append(['一目均衡表', close_price, '買い', '価格が雲の上にあるため'])
    elif close_price < latest_data['Senkou_span_A'] and close_price < latest_data['Senkou_span_B']:
        if latest_data['Tenkan-sen'] < latest_data['Kijun-sen']:
            evaluation.append(['一目均衡表', close_price, '強い売り', '価格が雲の下にあり、転換線が基準線を下回っているため'])
        else:
            evaluation.append(['一目均衡表', close_price, '売り', '価格が雲の下にあるため'])
    else:
        evaluation.append(['一目均衡表', close_price, '中立', '価格が雲の中にあるため'])

    # MACD
    if latest_data['MACD'] > latest_data['MACD_signal']:
        evaluation.append(['MACD', latest_data['MACD'], '買い', 'MACDがシグナルラインを上回っているため'])
    elif latest_data['MACD'] < latest_data['MACD_signal']:
        evaluation.append(['MACD', latest_data['MACD'], '売り', 'MACDがシグナルラインを下回っているため'])
    else:
        evaluation.append(['MACD', latest_data['MACD'], '中立', 'MACDとシグナルラインが交差しているため'])

    # RSI
    if latest_data['RSI'] < 30:
        evaluation.append(['RSI', latest_data['RSI'], '強い買い', 'RSIが30以下のため'])
    elif latest_data['RSI'] > 70:
        evaluation.append(['RSI', latest_data['RSI'], '強い売り', 'RSIが70以上のため'])
    elif 30 <= latest_data['RSI'] <= 50:
        evaluation.append(['RSI', latest_data['RSI'], '買い', 'RSIが30から50の間にあるため'])
    elif 50 < latest_data['RSI'] <= 70:
        evaluation.append(['RSI', latest_data['RSI'], '売り', 'RSIが50から70の間にあるため'])
    else:
        evaluation.append(['RSI', latest_data['RSI'], '中立', 'RSIが50のため'])

    # Stochastic (%K and %D)
    if latest_data['%K'] < 20:
        evaluation.append(['ストキャスティクス', latest_data['%K'], '強い買い', '%Kが20以下のため'])
    elif latest_data['%K'] > 80:
        evaluation.append(['ストキャスティクス', latest_data['%K'], '強い売り', '%Kが80以上のため'])
    elif 20 <= latest_data['%K'] <= 50:
        evaluation.append(['ストキャスティクス', latest_data['%K'], '買い', '%Kが20から50の間にあるため'])
    elif 50 < latest_data['%K'] <= 80:
        evaluation.append(['ストキャスティクス', latest_data['%K'], '売り', '%Kが50から80の間にあるため'])
    else:
        evaluation.append(['ストキャスティクス', latest_data['%K'], '中立', '%Kが50のため'])

    # RCI
    if latest_data['RCI'] < -80:
        evaluation.append(['RCI', latest_data['RCI'], '強い買い', 'RCIが-80以下のため'])
    elif latest_data['RCI'] > 80:
        evaluation.append(['RCI', latest_data['RCI'], '強い売り', 'RCIが80以上のため'])
    elif -80 <= latest_data['RCI'] <= -20:
        evaluation.append(['RCI', latest_data['RCI'], '買い', 'RCIが-80から-20の間にあるため'])
    elif 20 < latest_data['RCI'] <= 80:
        evaluation.append(['RCI', latest_data['RCI'], '売り', 'RCIが20から80の間にあるため'])
    else:
        evaluation.append(['RCI', latest_data['RCI'], '中立', 'RCIが-20から20の間にあるため'])

evaluate_indicators(latest_data)

# Convert the evaluation results to a DataFrame for better readability
evaluation_df = pd.DataFrame(evaluation, columns=['指標', '指標の値', '評価', '理由'])

evaluation_df

指標毎の評価結果

指標 指標の値 評価 理由
MA25 227.447001 中立 MA25とMA75が交差しているため
ボリンジャーバンド 219.550003 買い 価格がボリンジャーバンドの下限付近にあるため
一目均衡表 219.550003 強い売り 価格が雲の下にあり、転換線が基準線を下回っているため
MACD -4.613249 売り MACDがシグナルラインを下回っているため
RSI 42.226393 買い RSIが30から50の間にあるため
ストキャスティクス 20.853789 買い %Kが20から50の間にあるため
RCI 100.000000 強い売り RCIが80以上のため

総合評価

全体的な評価としては「中立」ですの! 以下の理由に基づきます:

  • 強い売りの評価が2つ、一目均衡表とRCI。
  • 売りの評価が1つ、MACD。
  • 買いの評価が3つ、ボリンジャーバンド、RSI、ストキャスティクス。
  • 中立の評価が1つ、MA25。

これらの結果から、買いの指標が多いものの、強い売りの評価も含まれており、全体的な方向性が明確でないため、中立と判断いたしましたの。 ​

おまけ:可視化してみる

ChatGPTが生成したコード

>可視化コード

import matplotlib.pyplot as plt

# Set the figure size
plt.figure(figsize=(15, 10))

# Plot MA25 and MA75
plt.subplot(3, 1, 1)
plt.plot(data['Date'], data['MA_20'], label='MA25')
plt.plot(data['Date'], data['MA'], label='MA75')
plt.title('Moving Averages (MA25 and MA75)')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()

# Plot Bollinger Bands
plt.subplot(3, 1, 2)
plt.plot(data['Date'], data['Close'], label='Close Price')
plt.plot(data['Date'], data['BB_Upper'], label='BB Upper')
plt.plot(data['Date'], data['BB_Lower'], label='BB Lower')
plt.title('Bollinger Bands')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()

# Plot MACD
plt.subplot(3, 1, 3)
plt.plot(data['Date'], data['MACD'], label='MACD')
plt.plot(data['Date'], data['MACD_signal'], label='MACD Signal')
plt.title('MACD')
plt.xlabel('Date')
plt.ylabel('Value')
plt.legend()

plt.tight_layout()
plt.show()

可視化結果

MA25の線が見つからないのだけ惜しい

可視化結果