コスパ志向のエンジニアブログ

趣味 システムトレード

Numpyだけで重回帰分析 機械学習 株価予測

scikit-learnを使えば簡単ですが最初はどのようになっているか、Numpyだけで重回帰分析も勉強になります。

説明変数は過去n日の対数リターンを使います。

データの読み込み

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

import yfinance as yf

code = '2432.T'
df = yf.download(code, start="2010-12-20", end="2023-02-03")
Date Open High Low Close Adj Close Volume
2023-01-27 00:00:00+09:00 1808.0 1813.0 1794.0 1796.0 1796.0 302400
2023-01-30 00:00:00+09:00 1793.0 1806.0 1791.0 1799.0 1799.0 341400
2023-01-31 00:00:00+09:00 1797.0 1821.0 1797.0 1818.0 1818.0 410100
2023-02-01 00:00:00+09:00 1819.0 1827.0 1801.0 1802.0 1802.0 367000
2023-02-02 00:00:00+09:00 1797.0 1803.0 1786.0 1789.0 1789.0 363500

ベースラインの作成、学習

def prepare_lags(_data):
  '''ラグ計算'''

  #入力として使用する日数(ラグというらしい)
  lags = 5
  cols = []
  for lag in range(1, lags + 1):
      col = f'lag_{lag}'
      _data[col] = _data['returns'].shift(lag)
      cols.append(col)
  _data.dropna(inplace=True)
  return _data, cols


def fit(_data):
  '''Numpyのlinalg.lstsqで重回帰分析'''

  return np.linalg.lstsq(_data[cols],
                                    _data['returns'],
                                    rcond=None)[0]

#対数リターン
df['returns'] = np.log(df['Adj Close'] / df['Adj Close'].shift(1))
#欠損値の行を削除
df.dropna(inplace=True)
# 学習、検証分割
train, test = train_test_split(df, train_size=0.7, shuffle=False)

data, cols = prepare_lags(train)
#係数生成
reg = fit(data)
print(reg)
array([ 0.06145026, -0.02996533,  0.00528902,  0.00780436,  0.01727923])

予測

np.dot: 内積

np.sign: 引数にnumpy.ndarrayを指定する。負の値は-1、正の値は1、0は0となるnumpy.ndarrayが返される。

# 予測
data['prediction'] = np.dot(data[cols], reg)
data['prediction_sign'] = np.sign(data['prediction'])

対数リターンと予測値を表示、線形回帰では予測値と大きくズレている

方向性が重要

data[['returns', 'prediction']].iloc[lags:].plot(figsize=(15, 6))

正解率

hits = np.sign(data['returns'] *  data['prediction']).value_counts()
hits.values[0] / sum(hits)
0.5067178502879078

バックテスト

#予測ポジションとリターンをかけて収益率を計算
data['strategy'] = data['prediction_sign'] * data['returns']
# 株価と戦略のグロスパフォーマンスをプロット
data[['returns', 'strategy']].cumsum().apply(np.exp).plot(figsize=(10, 6))
# 株価と戦略のグロスパフォーマンスを算出
data[['returns', 'strategy']].sum().apply(np.exp)
returns     0.845186
strategy    2.931524
dtype: float64

テストデータで検証

data, cols = prepare_lags(test)
data['prediction'] = np.dot(data[cols], reg)
data['prediction_sign'] = np.sign(data['prediction'])
hits = np.sign(data['returns'] *  data['prediction']).value_counts()
print('ヒット率: ', hits.values[0] / sum(hits))

data['strategy'] = data['prediction_sign'] * data['returns']
data[['returns', 'strategy']].cumsum().apply(np.exp).plot(figsize=(10, 6))
data[['returns', 'strategy']].sum().apply(np.exp)
ヒット率: 0.5092378752886836
returns     0.850081
strategy    2.575374
dtype: float64

まとめ

かなり勉強になるのでおすすめです。