マイクロソフトの株価分析

# はじめに
:::note warn
このコードは、株価の動きを予測するための一例であり、実際の投資判断には十分な知識と経験が必要です。また、過去のデータに基づく予測は、将来の結果を保証するものではありません。投資に関する決定は、自己責任で行ってください。このコードは、投資アドバイスを提供するものではありません。ご了承ください。
:::
:::note info
インフォメーション
このブログはAidemy Premiumのカリキュラムの一環で、受講修了条件を満たすために公開しています
:::

今回、上にも書いてある通り、Aidemy Premiumのデータ分析講座を受講した上で成果物の作成を行いました。
Aidemy Premiumを受講した理由としては、データ分析、機械学習、Pythonを学び、新たなキャリアの活路になるかと思ったのがっきかけです。
# 目的

タイトルにもあるようにマイクロソフトの株価をpythonを使って、株価データをロジスティック回帰で各テクニカル指標を元に機械学習モデルを訓練させ、その売買の精度を計算するものです。
また、経緯としては転職や副業などで株価の分析や予想といったものが活用できたらいいなと思ったので今回取り組んでみました。

# 大まかな流れ
①　マイクロソフトの株価を取得
②　各データの前処理
③　モデルの構築
④　月曜日だけデータ抽出した場合の正解率

# ①マイクロソフトの株価を取得
取得したあとにデータのタイプや数値の確認を行います。
最後に株価の変動をグラフで表示します。

~~~
import numpy as np
import pandas as pd
import datetime
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (accuracy_score,roc_curve,roc_auc_score,auc,
    confusion_matrix,
)
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
~~~

~~~
df =pd.read_csv('/kaggle/input/microsoft-stock-price2000-2023/MSFT(2000-2023).csv',  parse_dates=['Date'])
df
~~~

![スクリーンショット 2024-06-10 213643.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/c29d4017-f44c-8c79-0a20-15e1cdc35493.png)
カラムのそれぞれの意味は以下のとおりです。

・Open：始値
・High：高値
・Low：安値
・Close：終値
・Adj Close：調整後終値
・Volume：出来高（1日に取引が成立した株の数）

~~~
df = df.set_index('Date')
df.describe()
~~~
![スクリーンショット 2024-06-10 213827.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/a2c711fd-a997-967b-1875-81cc6aa4bca9.png)

~~~
df.dtypes
~~~
![スクリーンショット 2024-06-10 213905.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/bdd74cce-28bc-8898-b630-7fd8c16ce54c.png)

~~~
round(df.isnull().sum() / len(df) * 100, 2)
~~~
![スクリーンショット 2024-06-10 214201.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/7521457e-5ce2-f1fc-c13b-6ce18d656438.png)

~~~
fig, ax = plt.subplots(figsize=(16,8))
plt.title(f'Stock Trend Over The Years - MSFT')
plt.ylabel('Price in USD')
plt.xlabel('Date')
ax.plot(df['Close'], label = 'Close Price', alpha = 0.9, color = 'blue')
~~~
![__results___11_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/3a0917f1-60aa-6919-cb55-3b24b8ef5910.png)

# ②各データの前処理(テクニカル指標の処理)
今回は2022年の間の株の変動を学習させるデータ処理を行います。
テクニカル指標とは価格や出来高の推移をグラフ化したチャートの形状などから、将来の値動きを予想する分析で使われる指標です。過去のデータからパターンを表したもので、分析で潜在的な売りまたは買いの提案を予測するために使用されます。
今回使用するのはRSI、MACD、SMA、EMAの４つです。
~~~
start_date = "2022-01-01"
end_date = "2023-01-01"
df = df.loc[start_date:end_date]
df
~~~
![スクリーンショット 2024-06-10 214512.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/515bf811-a3f3-3d8c-175c-a01a761f6bb5.png)
### RSIの構築
RSIとは日本語で「相対力指数」と呼びます。簡単に解説すると、売られすぎか買われすぎかを判断するためのテクニカル指標です。
一般的にRSIが70％を超えた場合は買われすぎと判断でき、売りのサインとなります。逆に30%を下回った場合は売られすぎと判断でき、買いのサインとなります。

例えば上昇トレンドが発生した場合、RSIが70％を超えていなければまだまだ上昇余地はあり、70％を超えていれば上昇トレンドの転換と分析することが可能です。
~~~
def calculate_rsi(data, window=14):
    delta = data["Close"].diff(1)
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)

    avg_gain = gain.rolling(window=window, min_periods=1).mean()
    avg_loss = loss.rolling(window=window, min_periods=1).mean()

    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))

    return rsi


df["RSI"] = calculate_rsi(df)
~~~
### MACDの構築
MACDとは日本語訳で「移動平均線収束拡散」といいます。短期の移動平均線と中長期の移動平均線を使用することで、買いと売りを判断する手法です。そのため値動きにより素早く反応する性質を持ち、トレンドの初動を予測するのに役立つテクニカル指標です。
~~~
def calculate_macd(data, short_window=12, long_window=26):
    ema_short = data["Close"].ewm(span=short_window, min_periods=1, adjust=False).mean()
    ema_long = data["Close"].ewm(span=long_window, min_periods=1, adjust=False).mean()

    macd = ema_short - ema_long
    signal_line = macd.ewm(span=9, min_periods=1, adjust=False).mean()

    return macd, signal_line


df["MACD"], df["Signal_Line"] = calculate_macd(df)
~~~
### SMAの構築
SMA（単純移動平均線）は、株価分析における基本的なテクニカル指標の一つです。SMAは、一定期間の終値の平均値を結んだ線を指します。例えば、5日間のSMA（5日単純移動平均線）であれば、当日の終値が確定している場合、当日を含む過去5日間の終値の平均値を表します。
~~~
def calculate_sma(data, window=50):
    sma = data["Close"].rolling(window=window, min_periods=1).mean()
    return sma


df["SMA"] = calculate_sma(df)
~~~
### EMAの構築
EMAとは指数平滑移動平均線のことを言います。EMAは直近の終値に強く比重を掛けて計算するため、一般的なSMA（単純移動平均線）よりも早くトレンドに反応する特徴があります。
~~~
def calculate_ema(data, window=12):
    ema = data["Close"].ewm(span=window, min_periods=1, 
              adjust=False).mean()
    return ema


df["EMA"] = calculate_ema(df)
~~~
### 各テクニカル指標の設定、売買のシグナルを生成します。
~~~
df["Signal"] = np.where(
    (df["RSI"] > 30) & (df["MACD"] > df["Signal_Line"])
    & (df["Close"] > df["SMA"]) & (df["Close"] > df["EMA"]),1,0)

df = df.dropna()
~~~

# ③モデルの構築
ロジスティック回帰モデルを使用して各テクニカル指標を使用して訓練を行い、その精度を計算してモデルの重要な特徴を可視化します。
~~~
def build_model():
# 訓練データとテストデータに分ける
    train_size = int(0.8 * len(X))
    X_train, X_test, y_train, y_test = (
        X[:train_size],
        X[train_size:],
        y[:train_size],
        y[train_size:],
    )

    # ロジスティック回帰モデルを作成して訓練する
    model = LogisticRegression()
    model.fit(X_train, y_train)

    # テストデータの入力
    y_pred = model.predict(X_test)

    # 各テクニカル指標を元に精度の計算
    accuracy = accuracy_score(y_test, y_pred)
    
    plt.figure(figsize=(4, 2))
    coef_values = list(abs(model.coef_[0]))
    feature_names = ["RSI", "MACD", "SMA", "EMA"]  
    plt.barh(feature_names, sorted(coef_values, reverse=False), 
              color="purple")
    plt.xlabel("Coefficient Value")
    plt.ylabel("Feature")
    plt.title("Variable Importance (Coefficients)")
    #plt.show()

    # ROC曲線とAUCを計算する
    y_prob = model.predict_proba(X_test)[:, 1]
    fpr, tpr, _ = roc_curve(y_test, y_prob)
    roc_auc = auc(fpr, tpr)

    # 混同行列を計算してプロットする
    cm = confusion_matrix(y_test, y_pred)
    plt.figure(figsize=(4, 3))
    sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", annot_kws={"size": 8})
    plt.xlabel("Predicted")
    plt.ylabel("Actual")
    plt.title("Confusion Matrix")
    plt.show()

    # 取引戦略のテスト
    df["Strategy_Return"] = df["Close"].pct_change() * df["Signal"].shift(1)
    df["Buy_Hold_Return"] = df["Close"].pct_change()

    # リターンの計算
    df["Cumulative_Strategy_Return"] = (1 + df["Strategy_Return"]).cumprod()
    df["Cumulative_Buy_Hold_Return"] = (1 + df["Buy_Hold_Return"]).cumprod()

    # 株価終値、RSI、MACD、SMA、EMAのプロット
    plt.figure(figsize=(12, 6))
    plt.subplot(3, 1, 1)
    plt.plot(df["Close"], label="Close Price", color="blue")
    plt.title(f"{stock_symbol} Stock Price")
    plt.legend()


    # 機械学習モデルによって生成された売買シグナルのプロット
    plt.figure(figsize=(12, 4))
    plt.plot(
        df.index[train_size:], y_pred, label="ML Model Signal", color="green", marker="o"
    )
    plt.title(f"{stock_symbol} Buy/Sell Signal (ML Model)")
    plt.xlabel("Date")
    plt.ylabel("Signal (1=Buy, 0=Hold/Sell)")
    plt.legend()

    # ROC曲線をプロット
    plt.figure(figsize=(4, 3))
    plt.plot(
        fpr,
        tpr,
        color="darkorange",
        lw=2,
        label="ROC curve (area = {:.2f})".format(roc_auc),
    )
    plt.plot([0, 1], [0, 1], color="navy", lw=2, linestyle="--")
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel("False Positive Rate")
    plt.ylabel("True Positive Rate")
    plt.title("Receiver Operating Characteristic (ROC)")
    plt.legend(loc="lower right")

    # テクニカル指標と買いとステイのリターンのプロット
    plt.figure(figsize=(12, 6))
    plt.plot(
        df.index, df["Cumulative_Strategy_Return"], label="Strategy Return", color="green"
    )
    plt.plot(
        df.index,
        df["Cumulative_Buy_Hold_Return"],
        label="Buy and Hold Return",
        color="blue",
    )
    plt.title("Cumulative Returns")
    plt.xlabel("Date")
    plt.ylabel("Cumulative Return")
    plt.legend()
    
    # Display accuracy
    print(f"Model Accuracy: {accuracy * 100:.2f}%")

    plt.show()
~~~

~~~
X = df[["RSI", "MACD", "SMA", "EMA"]].values
y = df["Signal"].values
~~~
### 各テクニカル指標から測定
~~~
build_model()
~~~
![スクリーンショット 2024-06-10 222506.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/5f523c7a-5fa6-a60c-3757-72cdb08a6d12.png)
![スクリーンショット 2024-06-10 222536.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/5ac64cb3-f562-b4e3-d901-9aa1d875de33.png)
![スクリーンショット 2024-06-10 222558.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785851/9a51e28b-818a-54f3-f58b-f046b3437c8d.png)

# ④月曜日だけデータ抽出した場合の正解率
月曜日のデータだけを抽出し、次の日の終値が上昇したかどうかを示すラベルを作成しています。これらのデータを用いて、新たにモデルを訓練してその精度を計算しています。
~~~
# データの読み込み
df = pd.read_csv('/kaggle/input/microsoft-stock-price2000-2023/MSFT(2000-2023).csv')

df["RSI"] = calculate_rsi(df)
df["MACD"], df["Signal_Line"] = calculate_macd(df)

df["SMA"] = calculate_sma(df)
df["EMA"] = calculate_ema(df)

# 'Date'列をdatetime型に変換
df['Date'] = pd.to_datetime(df['Date'])

# 曜日を表す新しい列を作成（月曜日が0、日曜日が6）
df['Day_of_Week'] = df['Date'].dt.dayofweek

#月曜日のデータだけを抽出
monday_df = df[df['Day_of_Week'] == 0]

# ラベルを作成（次の日の終値が上昇したら1、それ以外は0）
monday_df['Label'] = np.where(monday_df['Close'].shift(-1) > monday_df['Close'], 1, 0)

monday_df = monday_df.dropna()

# 特徴量とラベルを分割
X = monday_df[["RSI", "MACD", "SMA", "EMA"]].values
y = monday_df['Label'].values

# モデルを作成して訓練
model = LogisticRegression()
model.fit(X, y)

# モデルの精度を計算
accuracy = model.score(X, y)
print(f"Model Accuracy: {accuracy * 100:.2f}%")
~~~
　Model Accuracy: 54.26%
### 結果
テクニカル指標を活用して買いかステイなどの分析を行い、モデルを活用した結果78%と比較的高い正解率を出せのではないかと思います。

また、最後に行ったあえて月曜日のデータのみを抽出し学習させた結果、あまり良くない正解率が出たので今回使用していないその他のテクニカル指標や訓練モデルの母数を増やすなどして学習させればもっと良い結果が得られるかもしれないと感じました。