Otama's Playground

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

ChatGPTを活用してテクニカル分析を自動化する方法:具体的手順と結果

以前以下の記事でテクニカル分析を行ってみましたが,毎回ChatGPTに生成してもらうのは面倒なので、自動でテクニカル指標の計算から評価結果の出力まで行うスクリプトを生成してもらうことにします。

otama-playground.hatenablog.com

スクリプトを生成する手順

以下のような流れでスクリプトの生成をします。ここで2~4の間に手動フェーズを挟んでいる理由は2つあります。1つ目は出力が長くなると精度が悪化する場合があるため、2つ目は途中で出力結果の妥当性チェックを行うためです。

  1. まず、テクニカル分析に使用する指標を選ぶ
  2. 指標を計算するコードをChatGPTに作成してもらう
  3. 2のコードを一度手元で実行して値を求めるとともに、エラーが出ないかなど確認。
  4. 3の結果をChatGPTに渡して買いか売りかを判断するスクリプトを書いてもらう
  5. 2と4のコードを統合して一つのpythonスクリプトにする。
    • ChatGPTに任せてもよいです
    • pythonを書ける方であれば手動で行った方が安心かもしれません

実行結果

2. 指標を計算するコードをChatGPTに作成してもらう

以下の指標を計算するpythonコードを生成してください。

指標を計算する部分は関数として分離してください。
* 移動平均線
* ボリンジャーバンド
* 一目均衡表
* MACD
* RSI
* ストキャスティクス
* RCI

>生成されたスクリプト

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. コードを一度手元で実行して値を求めるとともに、エラーが出ないかなど確認。

移動平均のレンジが20のみだったため、手動で25と75に修正しました。

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

>修正後のスクリプト

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()

4. 買いか売りかを判断するスクリプトを書いてもらう

テクニカル分析を行いました。

以下の指標を計算した結果が含まれているため、それぞれについて最新の日付を参照し,強い売り、売 
り、中立、買い、強い買いの5段階評価するスクリプトを書いてください。
* MA25
* MA75
* ボリンジャーバンド
* 一目均衡表
* MACD
* RSI
* ストキャスティクス
* RCI

最後にそれぞれの指標について評価した結果を表としてまとめること。表のカラム は,指標、指標の値、評価、理由の4列にしてください。まとめた結果を俯瞰し、強い売り、売り、中立、買い、強い買いの5段階評価を行ってくださ い。そう評価した理由も教えてください

3で得られたCSVを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_25'] > latest_data['MA_75']:
        evaluation.append(['MA', str(latest_data['MA_25']) + "/" + str(latest_data['MA_75']), '買い', 'MA25がMA75を上回っているため'])
    elif latest_data['MA_25'] < latest_data['MA_75']:
        evaluation.append(['MA', str(latest_data['MA_25']) + "/" + str(latest_data['MA_75']), '売り', 'MA25がMA75を下回っているため'])
    else:
        evaluation.append(['MA', str(latest_data['MA_25']) + "/" + str(latest_data['MA_75']), '中立', '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

以下のような出力が得られました。特に問題点はなさそうです。

指標 指標の値 評価 理由
MA 139.07759979248047/144.1038656616211 売り MA25がMA75を下回っているため
ボリンジャーバンド 129.929993 買い 価格がボリンジャーバンドの下限付近にあるため
一目均衡表 129.929993 強い売り 価格が雲の下にあり、転換線が基準線を下回っているため
MACD -4.416427 売り MACDがシグナルラインを下回っているため
RSI 31.430277 買い RSIが30から50の間にあるため
ストキャスティクス 16.846432 強い買い %Kが20以下のため
RCI 100.0 強い売り RCIが80以上のため

5. 2と4のコードを統合して一つのpythonスクリプトにする

今回のコンセプトはChatGPTに生成してもらうことなので、ChatGPTに統合してもらいました。

>最終的なコード

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 evaluate_indicators(latest_data):
    evaluation = []
    close_price = latest_data['Close']
    # MA25 and MA75
    if latest_data['MA_25'] > latest_data['MA_75']:
        evaluation.append(['MA', str(latest_data['MA_25']) + "/" + str(latest_data['MA_75']), '買い', 'MA25がMA75を上回っているため'])
    elif latest_data['MA_25'] < latest_data['MA_75']:
        evaluation.append(['MA', str(latest_data['MA_25']) + "/" + str(latest_data['MA_75']), '売り', 'MA25がMA75を下回っているため'])
    else:
        evaluation.append(['MA', str(latest_data['MA_25']) + "/" + str(latest_data['MA_75']), '中立', '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の間にあるため'])

    return evaluation

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=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('/path/to/your/outputfile.csv')

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

    # Evaluate the latest data
    evaluation = evaluate_indicators(latest_data)

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

    print(evaluation_df)

if __name__ == '__main__':
    main()

おまけ:可視化スクリプトも生成してみる

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()

可視化結果

可視化結果

最後に

テクニカル指標の計算から実際の評価までの全部を行うスクリプトをChatGPTに生成してもらうことにより、今後はChatGPTを使用せずにスクリプトを実行するだけで評価が可能になります。

毎回生成結果の妥当性チェックをする必要性もなくなるので、結構嬉しいんじゃないかなと思います。気になる方はぜひ試してみてください。

Appendix

指標毎の評価基準(参考)

指標 強い買い 買い 中立 売り 強い売り
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以上