【Copilot利用】LightGBMで株価予測をやってみた🎵

最近のGPTの進化は凄まじいということで、株価予測のコードを以下のとおり、Copilotに出力してもらいました。
詳細は、下の余談の順にやりとりしています。
最後の質問
「実際に株価データを使って予測したいです。」
回答
「株価データを使った予測を行うには、Pythonを活用するのが一般的です。Pythonには、株価分析や予測に役立つ多くのライブラリがあります。以下は、株価データを取得し、機械学習モデルを用いて予測を行う基本的なPythonコードの例です。

まず、必要なライブラリをインストールします。pandasはデータ操作、pandas_datareaderは株価データの取得、matplotlibはデータの可視化、sklearnは機械学習モデルの構築と評価、lightgbmはLightGBMモデルの実装に使用します。
```.py
# 必要なライブラリのインポート
import numpy as np
import pandas as pd
import pandas_datareader.data as web
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from lightgbm import LGBMRegressor

# 株価データの取得
# ここでは例としてAppleの株価データを取得します
ticker = 'AAPL'
start_date = '2023-01-01'
end_date = '2024-01-01'
data = web.DataReader(ticker, 'yahoo', start_date, end_date)

# 特徴量とターゲットの作成
# ここでは終値を予測するために、終値を1日シフトさせたものをターゲットとします
data['Target'] = data['Close'].shift(-1)
features = ['Open', 'High', 'Low', 'Close', 'Volume']

# データセットの分割
X = data[features].iloc[:-1]  # 最後の行はターゲットがNaNなので除外
y = data['Target'].iloc[:-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# モデルの訓練
model = LGBMRegressor(n_estimators=100, learning_rate=0.1, random_state=42)
model.fit(X_train, y_train)

# 予測と評価
predictions = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, predictions))
print(f'RMSE: {rmse}')

# 予測結果のプロット
plt.figure(figsize=(10, 5))
plt.plot(y_test.index, y_test, label='Actual')
plt.plot(y_test.index, predictions, label='Predicted')
plt.legend()
plt.show()
```
このコードは、Appleの株価データを取得し、終値を予測するためのLightGBMモデルを訓練しています。予測結果はRMSE（Root Mean Squared Error）で評価し、実際の終値と予測値をプロットして比較しています。

実際に予測を行う際には、適切な特徴量の選択、モデルのパラメータチューニング、さらにはデータの前処理など、多くのステップが必要です。また、株価予測は非常に複雑であり、多くの外部要因に影響されるため、予測はあくまで参考程度に留めるべきです123。

このコードを参考に、ご自身のデータセットに適用してみてください。もし追加の質問やサポートが必要であれば、お気軽にお尋ねください。どうぞご活用ください！
」
### コード改変
実は、とりあえず...動きません。
ということで、最終的に以下のように変更しました。
まず、株価取得はyfinanceを利用
```lib.py
# 必要なライブラリのインポート
import numpy as np
import pandas as pd
import pandas_datareader.data as web
import yfinance as yf
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from lightgbm import LGBMRegressor

def get_stock_yf(stock,start,end):
    df = yf.download(tickers=stock, start=start, end=end)
    return df
```
上のCopilotのコードは、学習時にデータをシャッフルしてしまうので、それを正しい日付順に直すためにdfに格納するためにpd.DataFrameを定義しておきます。
銘柄は、爆下げ中のNTTとしました。
日付は、今どきの日付
このコードの方針として大切なのは、以下のコード
つまり、常に一日ずらして予測（その行を上にずらす）
参考
[pandasでデータを行・列（縦・横）方向にずらすshift](https://udemy.benesse.co.jp/development/python-work/python-stock-price.html)

```.py
# ここでは終値を予測するために、終値を1日シフトさせたものをターゲットとします
data['Target'] = data['Close'].shift(-1)
```
利用するfeaturesは、その日の以下のデータですが、一応株価'Open','High', 'Low','Close'の内どれかが入っていればだいたい予測してくれました。
```
features = [ 'Open','High', 'Low','Close','Volume']
```
ということで、学習までは以下のとおり。
```.py
df = pd.DataFrame()

# 株価データの取得
# ここでは例としてAppleの株価データを取得します
ticker = '9432.T'
start_date = '2018-01-01'
end_date = '2024-06-07'
# data = web.DataReader(ticker, 'yahoo', start_date, end_date)
data = get_stock_yf(ticker, start_date, end_date)

# 特徴量とターゲットの作成
# ここでは終値を予測するために、終値を1日シフトさせたものをターゲットとします
data['Target'] = data['Close'].shift(-1)
features = [ 'Open','High', 'Low','Close','Volume'] # 'Open','High', 'Low','Close'

# データセットの分割
X = data[features].iloc[:-1]  # 最後の行はターゲットがNaNなので除外
y = data['Target'].iloc[:-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# モデルの訓練
model = LGBMRegressor(n_estimators=10000, learning_rate=0.01, random_state=42)
model.fit(X_train, y_train)
```
predictionsを上で定義したdfに格納し、ソートしたものをdf_と定義
```.py
# 予測と評価
predictions = model.predict(X_test)
df['predictions'] = predictions
df['Date'] = y_test.index
df_ = df.sort_values('Date', ascending = True)
```
以下、rmseを求めて、グラフ描画します。
```.py
rmse = np.sqrt(mean_squared_error(y_test, predictions))
print(f'RMSE: {rmse}')

# 予測結果のプロット
plt.figure(figsize=(10, 5))
y_test_ = y_test.sort_index(ascending=True)
#predictions_ = predictions.sort_index(ascending = True)
plt.plot(y_test_.index, y_test_, label='Actual')
plt.plot(df_['Date'], df_['predictions'], label='Predicted')
#plt.plot(y_test, predictions)
plt.legend()
plt.show()
```

![stock_yosoku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/a1d0c817-b8ba-7037-09e5-fe8582b38787.png)

### 遷移領域
```.py
plt.plot(y_test_, df_['predictions'])
```
まあ、大体あってるみたい。
※それにしても遷移領域というか飛びがあって怖いわな

![actual_pred.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/38948edf-4b96-5762-c61f-5cad0d6609c5.png)

上の絵はつまり以下のように株価の頻度分布を書くと遷移領域見えて、今どの領域にあるかがわかるという話なんだな。
```histgram.py
data['Close'].hist(bins=100).plot()
```
![hist_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/1bc49688-55b0-f819-c873-ab7a11cc4099.png)
```y_test_hist.py
y_test_.hist(bins=100).plot()
```
![hist_y_test_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/b7fa502a-ea21-ea21-3e1c-0fbdc46aa537.png)
```predictions_.py
df_['predictions'].hist(bins=100).plot()
```
![hist_pred.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/e7312886-9327-44d2-ccf5-96e2f4fca166.png)

Copilot使わなければ、以下の参考から。。。精度だしているけど、あんまり意味ないような気がするので、今回はパスします。
参考
[Pythonを使った株価のデータ取得や分析・予測方法を徹底解説！](https://udemy.benesse.co.jp/development/python-work/python-stock-price.html)

### 余談
以下、実際の株価評価のコードにたどり着くまでのやりとりを記述します。
質問
「Pythonで株価評価を行う際のベストプラクティスは？」
回答
「Pythonで株価評価を行う際のベストプラクティスには、以下のようなポイントがあります：
以下省略」
質問
「LightBGMを利用して、現在までの過去一年の株価変動、PER、ROE、EPSを利用して、来週の株価を予測しようとしたいと思っています。どういうコードでやったらいいでしょう。」
回答；一応まともだが、PER、ROE、EPSのデータ取得がいまいち不明なので、今回はパス
「省略」
質問
「株価予測モデルにおける特徴量エンジニアリングについて詳しく知りたいです。」
回答
「株価予測モデルにおける特徴量エンジニアリングは、モデルの予測精度を向上させるために非常に重要なプロセスです。特徴量エンジニアリングとは、生データから有益な情報を抽出し、モデルが学習しやすい形式に変換する作業を指します。以下に、株価予測モデルでよく用いられる特徴量エンジニアリングの手法をいくつか紹介します：

テクニカル指標の利用：
移動平均、ボリンジャーバンド、MACD（Moving Average Convergence Divergence）などのテクニカル指標を特徴量として使用します。
ファンダメンタル分析：
PER、ROE、EPSなどの財務指標を特徴量として組み込みます。
時系列データの処理：
株価の変動は時間に依存するため、ラグ特徴量（過去の価格や指標の値）を作成します。
外部データの統合：
為替レートや金利、経済指標など、株価に影響を与える可能性のある外部データを特徴量として追加します。
ニュースやSNSのセンチメント分析：
テキストデータからポジティブ、ネガティブな感情を抽出し、株価の動きに関連付けます。
多項式特徴量と交互作用特徴量：
既存の特徴量から新しい特徴量を生成するために、多項式や特徴量間の交互作用を考慮します。
特徴量選択：
モデルの過学習を防ぐために、特徴量の重要性に基づいて選択を行います。
データの正規化と標準化：
モデルの学習効率を高めるために、データを正規化または標準化します。
これらの手法を適切に組み合わせることで、株価予測モデルの性能を大幅に向上させることができます。特徴量エンジニアリングは、ドメイン知識とデータ分析のスキルが必要とされるため、専門的な知識を持つデータサイエンティストによって行われることが多いです」
質問
「具体的なpythonコードをおしえてください」
「。。。以下のコードは、大体動きそう
```code.py
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.ensemble import RandomForestRegressor

# データの読み込み
# 'stock_data.csv'は株価データを含むCSVファイルと仮定します
df = pd.read_csv('stock_data.csv')

# 特徴量とターゲットの分離
X = df.drop('Target', axis=1)  # 'Target'は予測したい株価
y = df['Target']

# データの標準化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 多項式特徴量の生成
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X_scaled)

# 特徴量選択
selector = SelectKBest(score_func=f_regression, k='all')
selector.fit(X_poly, y)
X_selected = selector.transform(X_poly)

# 訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X_selected, y, test_size=0.2, random_state=42)

# ランダムフォレストモデルの訓練
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

# テストデータでの予測
predictions = model.predict(X_test)

# 予測結果の表示
for true, pred in zip(y_test, predictions):
    print(f'Actual: {true}, Predicted: {pred}')
```    
」