[Python] SVMでの株価予測

SVMで翌日の株価の上昇/下降を予測します。

SVM

サポートベクターマシン: support vector machine, SVM)は、教師あり学習を用いるパターン認識モデルの一つである。分類回帰へ適用できる。1963年に Vladimir N. Vapnik, Alexey Ya. Chervonenkis が線形サポートベクターマシンを発表し[1]1992年に Bernhard E. Boser, Isabelle M. Guyon, Vladimir N. Vapnik が非線形へと拡張した。

出典: フリー百科事典『ウィキペディア(Wikipedia)』

クラス1とクラス-1に属する多数のデータがあり、それぞれデータをベクトルとして考えます。

このクラスを分類するための超平面を考えます。

結局クラス 1 に属するいくつかの点との距離の中の最小値とクラス -1 に属するいくつかの点との距離の中の最小値とが等しくなるように超平面が位置しなければならず、

このようにマージンが最大となるような超平面を特定するベクトル、つまり距離が最小値となるようなベクトルをサポートベクトルと呼び、この手法がサポートベクターマシーンと呼ばれる所以になります。

さらに、カーネル法と呼ばれる手法を使い非線形分類問題を線形分類問題に変換することで、SVMではそのままでは解くことができない非線形分類問題を解くことができます。

カーネル法(カーネルほう、: kernel method)はパターン認識において使われる手法の一つで、 判別などのアルゴリズムに組み合わせて利用するものである。よく知られているのは、サポートベクターマシンと組み合わせて利用する方法である。

出典: フリー百科事典『ウィキペディア(Wikipedia)』

サポートベクターマシンとは[カーネル法による非線形サポートベクターマシン]

Python では、sklearn のおかげで、SVMを簡単に扱うことができます。

1.4. Support Vector Machines

import numpy as np
import pandas as pd
import datetime

import numpy as np
import pandas as pd
import sklearn
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.metrics import confusion_matrix

from pandas_datareader import data as pdr
import yfinance as yf
import requests_cache
# override pandas_datareader
yf.pdr_override()
# casche for downloaded data
expire_after = datetime.timedelta(days=3)
session = requests_cache.CachedSession(cache_name='cache', backend='sqlite', expire_after=expire_after)


def calc_daily_pct_change(data):
    pct_change = data.pct_change()
    return pct_change

def create_dataset(stock, start, ende, lags=40):
    df =  pdr.get_data_yahoo(stock, start, end)

    df_lag = pd.DataFrame(index=df.index)
    df_lag['today'] = df['Adj Close']
    df_lag['Volume'] = df['Volume']

    for i in range(1, lags+1):
        df_lag[f'lag_close_{i}'] = df['Adj Close'].shift(i)
        df_lag[f'lag_vol_{i}'] = df['Volume'].shift(i)

    df_ret = pd.DataFrame(index=df_lag.index)
    df_ret['today'] = df_lag['today'].pct_change()

    for i in range(1, lags+1):
        df_ret[f'lag_close_{i}'] = df_lag[f'lag_close_{i}'].pct_change()
        df_ret[f'lag_vol_{i}'] = df_lag[f'lag_vol_{i}'].pct_change()

    df_ret.dropna(inplace=True)
    df_ret['up_or_down'] = np.sign(df_ret['today'])
    # drop rows where col 'up_or_down'==0 to redress the balance
    df_ret = df_ret[df_ret['up_or_down'] != 0]

    return df_ret

if __name__ == '__main__':
    start = datetime.datetime(2000, 1, 1)
    end = datetime.datetime(2020, 1, 1)

    data = create_dataset('GE', start, end)
    # print(data.describe())
    # print(data['up_or_down'].value_counts())

    X = data.drop(['today', 'up_or_down'], axis=1)
    y = data['up_or_down']

    start_test = datetime.datetime(2019, 1, 1)

    X_train = X[X.index < start_test]
    X_test = X[X.index >= start_test]
    y_train = y[y.index < start_test]
    y_test = y[y.index >= start_test]

    # standardize    
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)

    model = SVC(
        C=1000000.0, 
        kernel='rbf', 
        degree=3, 
        gamma=0.0001, 
        coef0=0.0, 
        shrinking=True, 
        probability=True, 
        cache_size=200, 
        class_weight=None, 
        max_iter=-1, 
        decision_function_shape='ovr', 
        random_state=0
    )
    model.fit(X_train_scaled, y_train)
    pred = model.predict(X_test_scaled)
    pred_prob = model.predict_proba(X_test_scaled)
    # [-1.  1.]
    # print(model.classes_)
    
    # Accuracy 0.520
    print(f'Accuracy {model.score(X_test_scaled, y_test):.3f}')
    #  [[62 58] 
    #  [61 67]]
    print(confusion_matrix(pred, y_test))
    # # 上手くいっていない    

ロジスティック回帰やk近傍法よりも正答率が悪くなってしまいました。

ハイパーパラメータを調整することで多少は改善できるかもしれませんが、単純な日次データのみから機械学習で株価を予測するのは難しいと思われます。