株価の推移データから分析用の指標を抽出する

  • URLをコピーしました!
目次

はじめに

ある企業の株価の変動を予測する際の分析に、テクニカル分析が挙げられます。値動きのデータを集めることができたら、トレンド分析につなげるための各種指標を集めていくことになります。

移動平均データを収集する

株価のデータについて、日足のデータはデータベースとして集めているとします。まずは移動平均を計算してみます。
移動平均(MA: Moving Average): これは一定期間の平均株価で、株価のトレンドを把握するのに役立ちます。短期的な移動平均(例えば5日間)と長期的な移動平均(例えば50日間または200日間)を比較することで、買い時と売り時を見つけることができます。

import pandas as pd
import sqlite3

# データベースへの接続
conn = sqlite3.connect('stock_data.db')

# データの取得
query = "SELECT * FROM stock_price"
df = pd.read_sql_query(query, conn)

# 日付をdatetime型に変換
df['date'] = pd.to_datetime(df['date'])

# 銘柄ごとに処理
tickers = df['ticker'].unique()
df_ma = pd.DataFrame()

for ticker in tickers:
    df_temp = df[df['ticker'] == ticker].copy()
    df_temp.set_index('date', inplace=True)

    # 移動平均の計算
    df_temp['ma_5'] = df_temp['close'].rolling(window=5).mean()
    df_temp['ma_10'] = df_temp['close'].rolling(window=10).mean()
    df_temp['ma_20'] = df_temp['close'].rolling(window=20).mean()

    df_ma = df_ma.append(df_temp.reset_index())

# テーブルへ保存
df_ma.to_sql('stock_price_ma', conn, if_exists='replace', index=False)

# 接続を閉じる
conn.close()

このスクリプトでは5日、10日、20日の移動平均を計算していますが、必要に応じてウィンドウのサイズを変更できます。

ボリンジャーバンドを求める

続いてそのまま求めた移動平均を使ってボリンジャーバンドも求めます。
ボリンジャーバンド: これは移動平均とその標準偏差を元に作られるバンドで、価格の上限と下限を示します。株価がバンドの外側に移動した場合、それは通常、価格が元に戻る可能性が高いという信号となります。

import pandas as pd
import sqlite3

# データベースへの接続
conn = sqlite3.connect('stock_data.db')

# データの取得
query = "SELECT * FROM stock_price"
df = pd.read_sql_query(query, conn)

# 日付をdatetime型に変換
df['date'] = pd.to_datetime(df['date'])

# 銘柄ごとに処理
tickers = df['ticker'].unique()
df_ma = pd.DataFrame()

for ticker in tickers:
    df_temp = df[df['ticker'] == ticker].copy()
    df_temp.set_index('date', inplace=True)

    # 移動平均の計算
    df_temp['ma_20'] = df_temp['close'].rolling(window=20).mean()
    
    # ボリンジャーバンドの計算
    df_temp['std_20'] = df_temp['close'].rolling(window=20).std()
    df_temp['upper_band'] = df_temp['ma_20'] + (df_temp['std_20'] * 2)
    df_temp['lower_band'] = df_temp['ma_20'] - (df_temp['std_20'] * 2)

    df_ma = df_ma.append(df_temp.reset_index())

# テーブルへ保存
df_ma.to_sql('stock_price_ma_bb', conn, if_exists='replace', index=False)

# 接続を閉じる
conn.close()

このスクリプトでは、20日間の移動平均と標準偏差を計算し、それを用いてボリンジャーバンド(上帯と下帯)を計算しています。ボリンジャーバンドの上帯は移動平均に2倍の標準偏差を加えたもの、下帯は移動平均から2倍の標準偏差を引いたものとなります。

相対力指数

次にRSIも求めます。
相対力指数(RSI: Relative Strength Index): RSIは価格の上昇圧力と下降圧力を比較する指標で、通常は14日間のデータを基に計算されます。RSIが70以上の場合、銘柄は「オーバーバイト(買われすぎ)」と見なされ、30以下の場合は「オーバーソールド(売られすぎ)」と見なされます。

import pandas as pd
import sqlite3

# データベースへの接続
conn = sqlite3.connect('stock_data.db')

# データの取得
query = "SELECT * FROM stock_price"
df = pd.read_sql_query(query, conn)

# 日付をdatetime型に変換
df['date'] = pd.to_datetime(df['date'])

# 銘柄ごとに処理
tickers = df['ticker'].unique()
df_ma = pd.DataFrame()

for ticker in tickers:
    df_temp = df[df['ticker'] == ticker].copy()
    df_temp.set_index('date', inplace=True)

    # 移動平均の計算
    df_temp['ma_20'] = df_temp['close'].rolling(window=20).mean()
    
    # ボリンジャーバンドの計算
    df_temp['std_20'] = df_temp['close'].rolling(window=20).std()
    df_temp['upper_band'] = df_temp['ma_20'] + (df_temp['std_20'] * 2)
    df_temp['lower_band'] = df_temp['ma_20'] - (df_temp['std_20'] * 2)

    # RSIの計算
    delta = df_temp['close'].diff()
    up, down = delta.copy(), delta.copy()
    up[up < 0] = 0
    down[down > 0] = 0

    average_gain = up.rolling(window=14).mean()
    average_loss = abs(down.rolling(window=14).mean())

    rs = average_gain / average_loss
    df_temp['rsi'] = 100 - (100 / (1 + rs))

    df_ma = df_ma.append(df_temp.reset_index())

# テーブルへ保存
df_ma.to_sql('stock_price_ma_bb_rsi', conn, if_exists='replace', index=False)

# 接続を閉じる
conn.close()

このスクリプトでは、まず価格の変化量(delta)を計算し、それを利益(up)と損失(down)に分けます。次に、14日間の平均利益(average_gain)と平均損失(average_loss)を計算し、その比率(rs)を求めます。最後に、RSIの公式を用いてRSIを計算します。

なお、上記のコードではシンプルなRSIの計算方法を用いていますが、より正確なRSIを求めるためには平滑化されたRSI(SMMA: Smoothed Moving Averageを使用)を計算する方法もあります。

MACD(Moving Average Convergence Divergence)

そのままMACDも求めます。
MACD(Moving Average Convergence Divergence): これは短期の移動平均と長期の移動平均の差を表す指標で、これら2つの平均の「収束(接近)」と「発散(離れる)」を追跡します。MACDが信号線(通常はMACDの9日間EMA)を上回った時は買い信号、下回った時は売り信号とされます。MACDは短期(12日)と長期(26日)の指数平滑移動平均(EMA)の差と、その信号線(9日のEMA)から計算します。

import pandas as pd
import sqlite3

# データベースへの接続
conn = sqlite3.connect('stock_data.db')

# データの取得
query = "SELECT * FROM stock_price"
df = pd.read_sql_query(query, conn)

# 日付をdatetime型に変換
df['date'] = pd.to_datetime(df['date'])

# 銘柄ごとに処理
tickers = df['ticker'].unique()
df_ma = pd.DataFrame()

for ticker in tickers:
    df_temp = df[df['ticker'] == ticker].copy()
    df_temp.set_index('date', inplace=True)

    # 移動平均の計算
    df_temp['ma_20'] = df_temp['close'].rolling(window=20).mean()
    
    # ボリンジャーバンドの計算
    df_temp['std_20'] = df_temp['close'].rolling(window=20).std()
    df_temp['upper_band'] = df_temp['ma_20'] + (df_temp['std_20'] * 2)
    df_temp['lower_band'] = df_temp['ma_20'] - (df_temp['std_20'] * 2)

    # RSIの計算
    delta = df_temp['close'].diff()
    up, down = delta.copy(), delta.copy()
    up[up < 0] = 0
    down[down > 0] = 0

    average_gain = up.rolling(window=14).mean()
    average_loss = abs(down.rolling(window=14).mean())

    rs = average_gain / average_loss
    df_temp['rsi'] = 100 - (100 / (1 + rs))

    # MACDの計算
    exp12 = df_temp['close'].ewm(span=12, adjust=False).mean()
    exp26 = df_temp['close'].ewm(span=26, adjust=False).mean()
    df_temp['macd'] = exp12 - exp26
    df_temp['signal'] = df_temp['macd'].ewm(span=9, adjust=False).mean()
    df_temp['histogram'] = df_temp['macd'] - df_temp['signal']

    df_ma = df_ma.append(df_temp.reset_index())

# テーブルへ保存
df_ma.to_sql('stock_price_ma_bb_rsi_macd', conn, if_exists='replace', index=False)

# 接続を閉じる
conn.close()

このスクリプトでは、まず12日と26日のEMA(exp12exp26)を計算し、その差(macd)を求めます。次に、macdの9日EMA(signal)を計算します。最後に、macdsignalの差(histogram)を求めます。これがMACDのヒストグラムになります。

まとめ

テクニカル分析の代表的な4種の指標を取り出しました。分析のためのデータを収集した後は、これらを組み合わせて戦略を立て、トレンド分析を行うことになります。本記事では指標の収集までの実装を行いました。

バーマン
プロダクトマネージャー
ソフトウェア開発に長く従事しています。
・機械学習のサンプルコード作成
・生成型AIから調べたことのまとめ
・これまでのビジネスで経験したことのまとめ
を記事として作成させていただいています。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次