NVIDIAの株価予測

こんにちは。私は数年前にNVIDIAの株を購入しました。昨年から急騰していますが、売却するか保有継続するか悩んでいます。そこで今後の株価について、勉強したてのPythonを使って予測してみたいと思います。

このブログはAidemy Premiumのカリキュラムの一環で、受講修了条件を満たすために公開しています


1. 株価データの取得

  • Yahoo! Finance を利用して株価データを取得するには yfinance ライブラリを使用します。

  • 特定の銘柄に対して、指定した期間の株価データをダウンロードし、それを CSV ファイルとして保存します。

import os 
import datetime as dt 
import pandas as pd 
import yfinance as yf
 
# 銘柄コードをNVIDIAに指定 
stock_code = "NVDA" 

# 2023年4月1日から今日までを指定 
first = '2023-04-01' last = dt.date.today() 

# 株価データを取得 
df = yf.download(stock_code, start=first, end=last) 

# csvデータとして保存 
df.to_csv('nvidia_stockdata.csv')

2. データの可視化

  • 株価データを時系列に沿って視覚化するために、matplotlib ライブラリを使用してグラフを描画します。

  • このステップでは、データの変動を把握しやすくするため、折れ線グラフを用いて株価の開場、高値、低値、終値を表示します。

import matplotlib.pyplot as plt 
# 銘柄コードをNVIDIAに指定 
stock_code = "NVDA" 
# 2023年9月1日から今日までを指定 
first = '2023-09-01' last = dt.date.today() 
# 株価データを取得 
df = yf.download(stock_code, start=first, end=last) 
# データを日付順に並び変え 
df.sort_values(by='Date', ascending=True, inplace=True) 
# グラフ化する株価のカラムのみを抽出 
df_graph = df[['Open', 'High', 'Low', 'Close']] 
# 折れ線グラフを描画 
df_graph.plot(kind='line') plt.show()

3. データの整理と特徴量の追加

  • データセットを整理し、機械学習モデルの精度を向上させるために追加の特徴量を計算します。

  • これには、終値の前日比や始値と終値の差額などが含まれます。

  • 特定の条件を満たすデータのみを選択して不要なデータを削除し、データのクリーニングを行います。

import pandas as pd 
# 日付のカラムを追加(インデックスが日付型であることを確認するか、明示的に変換) 
df['Date'] = pd.to_datetime(df.index) 
# データフレームをリセット 
df = df.reset_index(drop=True) 
# 'Volume' カラムが存在する場合のみ削除 
if 'Volume' in df.columns: df = df.drop('Volume', axis=1) 
# 週番号をデータフレームに追加 
df['weeknumber'] = df['Date'].dt.isocalendar().week 
# 翌日と当日の終値の差分を計算しデータフレームに追加 
df_shift = df.shift(-1) df['diff_Close'] = df_shift['Close'] - df['Close'] 
# 終値の前日比を計算しデータフレームに追加 
df_shift = df.shift(1) df['Close_rate'] = (df['Close'] - df_shift['Close']) / df_shift['Close'] 
# 始値と終値の差額をデータフレームに追加 
df['Trunk'] = df['Open'] - df['Close']

4. 予測モデルの構築

  • TensorFlowKeras を使用して LSTM ネットワークを構築し、株価の上昇・下降を予測します。

  • このプロセスには、標準化、モデルの訓練、そして検証データを使った評価が含まれます。

from keras.models import Sequential
from keras.layers import Dense, LSTM
from keras.layers import Dropout

def lstm_comp(df):
    model = Sequential()
    model.add(LSTM(256, activation='relu', batch_input_shape=(None, df.shape[1], df.shape[2])))
    model.add(Dropout(0.2))
    model.add(Dense(256, activation='relu'))
    model.add(Dropout(0.2))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model


5. 特徴量の準備と標準化

特徴量を準備し、標準化します。これには StandardScaler を使用します。
特徴量を同じスケールに標準化することで、モデルが各特徴量を公平に評価し、予測精度向上したいと思います。

from sklearn.preprocessing import StandardScaler
# 特徴量と目的変数を分ける
features = df[['Open', 'High', 'Low', 'Close', 'Close_rate', 'Trunk']]target = df['Close'].shift(-1) > df['Close'# 明日の終値が今日より高い場合は True
# NaN 値を除去
features = features[:-1] # 最後の行は目的変数が NaN になるので削除target = target[:-1] # 同様に最後の行を削除
# 特徴量の標準化
scaler = StandardScaler()
features_scaled = scaler.fit_transform(features)

6. モデルの構築と学習

  • モデルを構築し、データを学習します。TimeSeriesSplit を使用してデータを分割し、交差検証を行います。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
from keras.models import Sequential
from keras.layers import Dense, LSTM, Dropout


# 特徴量として 'Close' のインデックスを取得
close_index = list(features.columns).index('Close')


# 時系列データの分割とグラフの描画
tss = TimeSeriesSplit(n_splits=5)


# モデル構築のための関数を再定義(入力形状を調整)
def lstm_comp(input_shape):
 model = Sequential()
 model.add(LSTM(256, activation='relu', input_shape=input_shape))
 model.add(Dropout(0.2))
 model.add(Dense(256, activation='relu'))
 model.add(Dropout(0.2))
 model.add(Dense(1, activation='sigmoid'))
 model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model


fig, axes = plt.subplots(nrows=5, ncols=1, figsize=(10, 15), sharex=True)
results = []


for i, (train_index, test_index) in enumerate(tss.split(features_scaled)):
# 学習データと検証データを分ける
 X_train, X_test = features_scaled[train_index], features_scaled[test_index]
 y_train, y_test = target[train_index], target[test_index]


# モデルの学習
 model = lstm_comp((X_train.shape[1], 1))
 model.fit(X_train, y_train, epochs=10, batch_size=64, verbose=1)


# 予測と評価
 predictions = model.predict(X_test).flatten()
 predictions = np.where(predictions < 0.5, 0, 1)
 accuracy = accuracy_score(y_test, predictions)
 results.append(accuracy)


# 終値データを取得
 train_close_prices = features.iloc[train_index]['Close']
 test_close_prices = features.iloc[test_index]['Close']


# グラフの描画
 axes[i].plot(train_index, train_close_prices, color='blue', label='Training Data')
 axes[i].plot(test_index, test_close_prices, color='red', label='Validation Data')
 axes[i].set_title(f'Split {i+1} - Accuracy: {accuracy:.2f}')
 axes[i].set_ylabel('Close Price')


axes[0].legend()
plt.xlabel('Index')
plt.show()


# 交差検証の結果を表示
print(f'Mean Accuracy: {np.mean(results)}')

この一連のステップを通じて、実際の株価データを基にした予測モデルを構築し、そのモデルの精度を評価することができました。

Epoch 1/10
1/1 [==============================] - 2s 2s/step - loss: nan - accuracy: 0.5357
Epoch 2/10
1/1 [==============================] - 0s 28ms/step - loss: nan - accuracy: 0.4286
Epoch 3/10
1/1 [==============================] - 0s 28ms/step - loss: nan - accuracy: 0.4286
Epoch 4/10
1/1 [==============================] - 0s 28ms/step - loss: nan - accuracy: 0.4286
Epoch 5/10
1/1 [==============================] - 0s 26ms/step - loss: nan - accuracy: 0.4286
Epoch 6/10
1/1 [==============================] - 0s 25ms/step - loss: nan - accuracy: 0.4286
Epoch 7/10
1/1 [==============================] - 0s 27ms/step - loss: nan - accuracy: 0.4286
Epoch 8/10
1/1 [==============================] - 0s 26ms/step - loss: nan - accuracy: 0.4286
Epoch 9/10
1/1 [==============================] - 0s 28ms/step - loss: nan - accuracy: 0.4286
Epoch 10/10
1/1 [==============================] - 0s 28ms/step - loss: nan - accuracy: 0.4286
1/1 [==============================] - 0s 202ms/step
Epoch 1/10
1/1 [==============================] - 2s 2s/step - loss: nan - accuracy: 0.4151
Epoch 2/10
1/1 [==============================] - 0s 38ms/step - loss: nan - accuracy: 0.3962
Epoch 3/10
1/1 [==============================] - 0s 30ms/step - loss: nan - accuracy: 0.3962
Epoch 4/10
1/1 [==============================] - 0s 34ms/step - loss: nan - accuracy: 0.3962
Epoch 5/10
1/1 [==============================] - 0s 33ms/step - loss: nan - accuracy: 0.3962
Epoch 6/10
1/1 [==============================] - 0s 33ms/step - loss: nan - accuracy: 0.3962
Epoch 7/10
1/1 [==============================] - 0s 34ms/step - loss: nan - accuracy: 0.3962
Epoch 8/10
1/1 [==============================] - 0s 34ms/step - loss: nan - accuracy: 0.3962
Epoch 9/10
1/1 [==============================] - 0s 32ms/step - loss: nan - accuracy: 0.3962
Epoch 10/10
1/1 [==============================] - 0s 37ms/step - loss: nan - accuracy: 0.3962
1/1 [==============================] - 0s 181ms/step
Epoch 1/10
2/2 [==============================] - 2s 20ms/step - loss: nan - accuracy: 0.5000
Epoch 2/10
2/2 [==============================] - 0s 19ms/step - loss: nan - accuracy: 0.4359
Epoch 3/10
2/2 [==============================] - 0s 20ms/step - loss: nan - accuracy: 0.4359
Epoch 4/10
2/2 [==============================] - 0s 20ms/step - loss: nan - accuracy: 0.4359
Epoch 5/10
2/2 [==============================] - 0s 19ms/step - loss: nan - accuracy: 0.4359
Epoch 6/10
2/2 [==============================] - 0s 19ms/step - loss: nan - accuracy: 0.4359
Epoch 7/10
2/2 [==============================] - 0s 23ms/step - loss: nan - accuracy: 0.4359
Epoch 8/10
2/2 [==============================] - 0s 21ms/step - loss: nan - accuracy: 0.4359
Epoch 9/10
2/2 [==============================] - 0s 20ms/step - loss: nan - accuracy: 0.4359
Epoch 10/10
2/2 [==============================] - 0s 20ms/step - loss: nan - accuracy: 0.4359
1/1 [==============================] - 0s 198ms/step
Epoch 1/10
2/2 [==============================] - 2s 39ms/step - loss: nan - accuracy: 0.4563
Epoch 2/10
2/2 [==============================] - 0s 37ms/step - loss: nan - accuracy: 0.3981
Epoch 3/10
2/2 [==============================] - 0s 35ms/step - loss: nan - accuracy: 0.3981
Epoch 4/10
2/2 [==============================] - 0s 39ms/step - loss: nan - accuracy: 0.3981
Epoch 5/10
2/2 [==============================] - 0s 42ms/step - loss: nan - accuracy: 0.3981
Epoch 6/10
2/2 [==============================] - 0s 37ms/step - loss: nan - accuracy: 0.3981
Epoch 7/10
2/2 [==============================] - 0s 37ms/step - loss: nan - accuracy: 0.3981
Epoch 8/10
2/2 [==============================] - 0s 37ms/step - loss: nan - accuracy: 0.3981
Epoch 9/10
2/2 [==============================] - 0s 37ms/step - loss: nan - accuracy: 0.3981
Epoch 10/10
2/2 [==============================] - 0s 37ms/step - loss: nan - accuracy: 0.3981
1/1 [==============================] - 0s 271ms/step
Epoch 1/10
2/2 [==============================] - 2s 31ms/step - loss: nan - accuracy: 0.5625
Epoch 2/10
2/2 [==============================] - 0s 29ms/step - loss: nan - accuracy: 0.3906
Epoch 3/10
2/2 [==============================] - 0s 30ms/step - loss: nan - accuracy: 0.3906
Epoch 4/10
2/2 [==============================] - 0s 31ms/step - loss: nan - accuracy: 0.3906
Epoch 5/10
2/2 [==============================] - 0s 30ms/step - loss: nan - accuracy: 0.3906
Epoch 6/10
2/2 [==============================] - 0s 34ms/step - loss: nan - accuracy: 0.3906
Epoch 7/10
2/2 [==============================] - 0s 30ms/step - loss: nan - accuracy: 0.3906
Epoch 8/10
2/2 [==============================] - 0s 33ms/step - loss: nan - accuracy: 0.3906
Epoch 9/10
2/2 [==============================] - 0s 29ms/step - loss: nan - accuracy: 0.3906
Epoch 10/10
2/2 [==============================] - 0s 29ms/step - loss: nan - accuracy: 0.3906
1/1 [==============================] - 0s 215ms/step
Mean Accuracy: 0.592

7. 考察


提供された出力から、モデルが学習中にnan(Not a Number)という損失値(loss)を返していることが確認できます。これは一般的に数値的な問題、特に勾配の爆発(gradient explosion)や不適切なデータ処理によるものです。さらに、精度(accuracy)も安定しておらず、一貫したパフォーマンスを示していません。これにより、このモデルが将来の価格動向を予測するために現在信頼性が低いことが示されます。

モデルのトレーニング結果が信頼性を欠くため、現在のモデルを使用してこの銘柄の正確な価格動向を予測することは困難です。

今回は思ったような結果が出ませんでしたが、引き続き市場動向を注視しながら、定期的に同じような分析をしていきたいと思います。

8.追記

SARIMAモデルでも分析しました。2023年9月1日からのデータを基に2024年12月31日までのNVIDIA株価を予測するシナリオを想定します。


import pandas as pd
import yfinance as yf
from statsmodels.tsa.statespace.sarimax import SARIMAX
import matplotlib.pyplot as plt
import datetime

# 株価データの取得
stock_code = "NVDA"
start_date = "2023-09-01"
end_today = datetime.datetime.now().strftime('%Y-%m-%d')  # 今日の日付を取得
data = yf.download(stock_code, start=start_date, end=end_today)['Close']

# データの日付を確認
data.index = pd.to_datetime(data.index)

# 予測の終了日を設定
end_date = datetime.datetime(2024, 12, 31)

# 現在のデータの最後の日付から、予測の終了日までの日数を計算
forecast_steps = (end_date - data.index[-1]).days

# モデルの定義
model = SARIMAX(data, order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))

# モデルのフィッティング
result = model.fit(disp=False)

# ステップ数に基づいて予測
forecast = result.get_forecast(steps=forecast_steps)
forecast_df = forecast.summary_frame()

# 予測期間の日付インデックスを取得
forecast_index = pd.date_range(start=data.index[-1] + pd.Timedelta(days=1), periods=forecast_steps, freq='B')

# 予測結果のプロット
plt.figure(figsize=(12, 6))
plt.plot(data.index, data, label='Actual', color='blue')  # 実データ
plt.plot(forecast_index, forecast_df['mean'], label='Forecast', color='red', linestyle='--')  # 予測データ
plt.fill_between(forecast_index, forecast_df['mean_ci_lower'], forecast_df['mean_ci_upper'], color='pink', alpha=0.3)  # 信頼区間
plt.title('NVIDIA Stock Price Forecast up to December 31, 2024')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.show()


信頼区間が非常に広くなってしまいました。一方、予測平均は多少上向きになっているので、将来的に上昇する可能性はまだあるのかなと感じました。

この記事が気に入ったらサポートをしてみませんか?