重線形回帰を用いた翌日の株価予測
2022年10月21日の様々な指標を入力にして重線形回帰を用いて翌日の株価を予測してみた。ちなみに、結果が何かに利用できるものではないと思われる。なぜなら当日の株価と翌日の株価はそんなに変わらないのに対して、時価総額とかEPSには株価の要素が間接的に含まれているので、このままでは何も使えないだろうなというのが結論です。
ライブラリのインポート
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
import seaborn as sns
import japanize_matplotlib
sns.set(font='Hiragino Sans')
データの読み込み
raw_data = pd.read_csv('technical_stock_20221021.csv')
#主市場や指数採用銘柄は関係なさそうなので除外
raw_data = raw_data.drop(['主市場','指数採用銘柄'],axis=1)
raw_data.head()
前処理
変数の記述統計量の確認
raw_data.describe(include='all')
欠損値の扱い
raw_data.isnull().sum().sort_values(ascending=False)
5年前には上場してなかった等の理由でNaNのデータが数多くあるが、結果的に1000件もあると思われるので、NaNが含まれる銘柄は全て削除
data = raw_data.dropna(axis=0)
data.describe(include='all')
確率密度関数
sns.distplot(data['翌日株価'])
指数関数っぽい分布になっており、数多くの外れ値が含まれるため、上位の1%を除外する。
外れ値の処理
q = data['翌日株価'].quantile(0.99)
data_1 = data[data['翌日株価']<q]
data_1.describe(include='all')
sns.distplot(data_1['翌日株価'])
sns.distplot(data_1['EPS'])
q = data_1['EPS'].quantile(0.99)
data_2 = data_1[data_1['EPS']<q]
sns.distplot(data_2['EPS'])
sns.distplot(data_2['時価総額'])
q = data_2['時価総額'].quantile(0.99)
data_3 = data_2[data_2['時価総額']<q]
data_3.describe(include='all')
sns.distplot(data_3['時価総額'])
data_cleaned = data_3.reset_index(drop=True)
data_cleaned.describe(include='all')
data_corr = data_cleaned.corr()
sns.heatmap(data_corr, vmax=1, vmin=-1, center=0)
data_corr["翌日株価"].sort_values(ascending = False)
相関を求めてみると上記の通り、株価と翌日株価の相関は極めて高い。当たり前だが。
最小二乗法の前提の確認
線形回帰分析するにあたって、以下の条件が必要であることを先の投稿で述べた。これらが今回はどうかを調べてみる。
1, 自己相関がないこと。 こちらは、今回は時系列のデータではないので、自己相関はないと思われる。
2, 多重共線性が無いこと。 こちらはこのあとで確認
3, 線形性が無いこと。 こちらはこのあとで確認
4, 内成性が無いこと。 後ほど確認
線形性の確認
f, (ax1, ax2, ax3) = plt.subplots(1, 3, sharey=True, figsize =(15,3))
ax1.scatter(data_cleaned['EPS'],data_cleaned['翌日株価'])
ax1.set_title('翌日株価 and EPS')
ax2.scatter(data_cleaned['時価総額'],data_cleaned['翌日株価'])
ax2.set_title('翌日株価 and 時価総額')
ax3.scatter(data_cleaned['PSR'],data_cleaned['翌日株価'])
ax3.set_title('翌日株価 and PSR')
plt.show()
正直、線形性があるとは言えないので、線形回帰分析を進めるのには向かないと思われるがこのまま進めようかなと思う。
また、3つ目のPSRは外れ値を除外していないので、EPSや時価総額と比較すると外れ値が多く含まれることがわかる。本来はこれらも外れ値は除外した方がいいと思われる。
多重共線性の確認
from statsmodels.stats.outliers_influence import variance_inflation_factor
#多重共線性を調べる変数の指定。カテゴリ変数以外を全て指定
variables = data_cleaned[['証券コード', '時価総額', '売買単位', '信用残-買', '信用残-売',
'前週比-買', '前週比-売', 'PER', 'PER変化率-1年前', 'PER変化率-3年前', 'PER変化率-5年前',
'EPS', 'PBR', 'PBR変化率-1年前', 'PBR変化率-3年前', 'PBR変化率-5年前', '配当利回',
'配当利回-1年前', '配当利回-3年前', '配当利回-5年前', 'ROE', 'ROA', '売上高',
'売上高変化率-Y0Y', 'PSR', '売上高営業利益率', '売上高経常利益率', '売上債権回転率', '経常利益',
'経常利益変化率-YoY', '自己資本比率', '前期最終損益', '有利子負債自己資本利率', 'EV/EBITDA倍率',
'流動比率', '当座比率', 'PCFR', '棚卸資産回転率', '信用残/売買高レシオ', '株価',
'株価変化率-1営業日前', '株価変化率-5営業日前', '株価変化率-20営業日前', '株価変化率-3ヶ月前',
'株価変化率-6ヶ月前', '株価変化率-1年前', '株価変化率-3年前', '株価変化率-5年前', '年初来安値からの上昇率',
'年初来高値からの下落率', '平均売買代金-5日', '平均売買代金-20日', '出来高増加率-5営業日前',
'出来高増加率-20営業日前', '出来高増加率-3ヶ月前', '出来高増加率-6ヶ月前', '出来高増加率-1年前',
'売買代金増加率-5日', '売買代金増加率-20営業日前', '売買代金増加率-3ヶ月前', '売買代金増加率-6ヶ月前',
'売買代金増加率-1年前', '株価移動平均線からの乖離率-5営業日', '株価移動平均線からの乖離率-20営業日',
'株価移動平均線からの乖離率-75営業日', 'RSI', 'サイコロジカルライン', 'ノーマルストキャスティクス','翌日株価']]
vif = pd.DataFrame()
vif["VIF"] = [variance_inflation_factor(variables.values, i) for i in range(variables.shape[1])]
vif["Features"] = variables.columns
vif
VIF=1:多重共線性はない。
1 < VIF < 5: が問題ない。
5 < VIF: 多重共線性あり
になるらしい。ということで、5超を除外
multicollinearity_columns = vif[vif["VIF"]>5]["Features"].to_list()
#翌日株価が除外されると元も子もないので、それは残す
multicollinearity_columns.remove('翌日株価')
multicollinearity_columns
data_no_multicollinearity = data_cleaned.drop(multicollinearity_columns,axis=1)
ダミー変数の作成
カテゴリー変数をダミー変数に変換
data_preprocessed = pd.get_dummies(data_no_multicollinearity, drop_first=True)
data_preprocessed.head()
線形回帰モデル
入力とターゲットの宣言
targets = data_preprocessed['翌日株価']
inputs = data_preprocessed.drop(['翌日株価'],axis=1)
データの標準化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(inputs)
inputs_scaled = scaler.transform(inputs)
訓練データとテストデータの分割
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(inputs_scaled, targets, test_size=0.2, random_state=42)
回帰の作成
reg = LinearRegression()
reg.fit(x_train,y_train)
y_hat = reg.predict(x_train)
plt.scatter(y_train, y_hat, alpha=0.2)
plt.xlabel('Targets (y_train)',size=18)
plt.ylabel('Predictions (y_hat)',size=18)
plt.xlim(0,10000)
plt.ylim(0,10000)
plt.show()
sns.distplot(y_train - y_hat)
plt.title("Residuals PDF", size=18)
reg.score(x_train,y_train)
この結果から対して結果は良くないことがわかる。
重みとバイアス
reg.intercept_
reg.coef_
reg_summary = pd.DataFrame(inputs.columns.values, columns=['Features'])
reg_summary['Weights'] = reg.coef_
pd.options.display.max_rows = 999
weight = reg_summary.sort_values('Weights')
weight
テスト
y_hat_test = reg.predict(x_test)
plt.scatter(y_test, y_hat_test, alpha=0.2)
plt.xlabel('Targets (y_test)',size=18)
plt.ylabel('Predictions (y_hat_test)',size=18)
plt.xlim(0,10000)
plt.ylim(0,10000)
plt.show()
df_pf = pd.DataFrame(y_hat_test, columns=['Prediction'])
y_test = y_test.reset_index(drop=True)
df_pf['Target'] = y_test
df_pf['Residual'] = df_pf['Target'] - df_pf['Prediction']
df_pf['Difference%'] = np.absolute(df_pf['Residual']/df_pf['Target']*100)
df_pf.describe()
残差を見ると、それなりにばらつきがあることがわかる。おそらくそれなりに丁寧にデータを整理すればもうちょっと精度は良くなるのかなとも思う。
ディスカッション
コメント一覧
まだ、コメントがありません