目次
はじめに
機械学習の仕組みを使って、株の自動売買のシミュレーションをしてみます。モデルはサポートベクターマシン、指標はRSIを用いることとします。
シミュレーション条件
株価のデータは既に取得済みとします。テストデータは直近(2023/05/01まで)の2年間とします。学習データの生成期間はそれ以前の5年から15年、移動平均の期間も5日から50日と変動させ、2年で10万円の資産が増えるかどうか、を記録することとします。
サンプルコード
以下にサンプルコードを記載します。サポートベクターマシン(SVM)の主要なパラメータについて説明します。SVMでは主に次の二つのパラメータが重要です:
- C:誤分類に対するペナルティ(Cが大きいほど誤分類を許さない)
- gamma:決定境界の複雑さ(gammaが大きいほど境界は複雑になる)
ここでは、sklearnのGridSearchCV
を用いて、これらのパラメータの最適な組み合わせを見つける例を示します。この例では、Cとgammaのそれぞれについて複数の値を試し、クロスバリデーションを用いて最適なパラメータを選択します。
import sqlite3
import pandas as pd
import numpy as np
from sklearn import svm
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score
from ta.momentum import RSIIndicator
# SQLiteデータベースに接続
conn = sqlite3.connect('stock_data.db')
cur = conn.cursor()
# 全ての銘柄を取得
tickers = cur.execute('SELECT DISTINCT ticker FROM stock_price').fetchall()
results = pd.DataFrame(columns=['ticker', 'C', 'gamma', 'accuracy', 'final_money'])
# 各銘柄について処理
for ticker in tickers:
ticker = ticker[0]
# 銘柄のデータを取得
df = pd.read_sql(f'SELECT * FROM stock_price WHERE ticker = "{ticker}"', conn)
df['date'] = pd.to_datetime(df['date'])
df = df.set_index('date')
# RSIを計算
rsi_indicator = RSIIndicator(close = df["close"])
df['rsi'] = rsi_indicator.rsi()
# 株価の変化率を計算し、その符号(上がるか下がるか)をラベルとする
df['target'] = np.sign(df['close'].pct_change().shift(-1))
# 学習期間とテスト期間を設定
train_start_date = '2006-05-01'
train_end_date = '2021-05-01'
test_end_date = '2023-05-01'
# 学習データとテストデータに分割
train_df = df[train_start_date:train_end_date]
# NaNを前後の値で補完
train_df = train_df.fillna(method='ffill').fillna(method='bfill')
test_df = df[train_end_date:test_end_date]
# NaNを前後の値で補完
test_df = test_df.fillna(method='ffill').fillna(method='bfill')
# データを正規化
scaler = StandardScaler()
train_scaled = scaler.fit_transform(train_df[['close', 'rsi']])
test_scaled = scaler.transform(test_df[['close', 'rsi']])
# サポートベクターマシンでモデルを学習
parameters = {'kernel':('linear', 'rbf'), 'C':[1, 10], 'gamma':[0.1, 1]}
svc = svm.SVC()
clf = GridSearchCV(svc, parameters)
clf.fit(train_scaled, train_df['target'])
# テストデータで予測
test_df.loc[:, 'prediction'] = clf.predict(test_scaled)
# 精度を計算
accuracy = accuracy_score(test_df['target'], test_df['prediction'])
# シミュレーションで資金を計算
money = 100000 # スタート時の資金
shares = 0 # 株式の保有数
for i in range(len(test_df)):
# 予測による売買
if test_df['prediction'].iloc[i] == 1:
shares += money // test_df['close'].iloc[i]
money %= test_df['close'].iloc[i]
elif test_df['prediction'].iloc[i] == -1 and shares > 0:
money += shares * test_df['close'].iloc[i]
shares = 0
# 最終的な資産を計算
final_money = money + shares * test_df['close'].iloc[-1]
# 結果をデータフレームに追加
results = pd.concat([results, pd.DataFrame({'ticker': [ticker], 'C': [clf.best_params_['C']], 'gamma': [clf.best_params_['gamma']], 'accuracy': [accuracy], 'final_money': [final_money]})], ignore_index=True)
# 結果をデータベースに保存
results.to_sql('svm_rsi_result', conn, if_exists='replace')
# 接続解除
conn.close()
このコードでは、全ての銘柄コードについてサポートベクターマシンモデルを訓練し、最適なCとgammaのパラメータをグリッドサーチとクロスバリデーションで選択します。それぞれの銘柄について、最適なパラメータとその時のテストデータに対する精度を計算し、結果をデータフレームに保存します。
実行結果
上記のシミュレーション結果です。上位20社を掲載します。
企業名 | C | gamma | accuracy | 最終金額 |
---|---|---|---|---|
INPEX | 1 | 0.1 | 0.5214724 | ¥199,456 |
ヤクルト | 1 | 0.1 | 0.5255624 | ¥188,560 |
NTT | 1 | 0.1 | 0.5316973 | ¥162,443 |
第一三共 | 1 | 0.1 | 0.4969325 | ¥161,583 |
ソニーG | 10 | 1 | 0.5337423 | ¥152,092 |
東京海上 | 1 | 0.1 | 0.5276074 | ¥151,525 |
三菱重 | 1 | 0.1 | 0.4703476 | ¥146,625 |
日立 | 1 | 1 | 0.5071575 | ¥144,928 |
SOMPO | 1 | 0.1 | 0.5276074 | ¥137,992 |
丸紅 | 10 | 1 | 0.4846626 | ¥135,478 |
住友商 | 1 | 0.1 | 0.4785276 | ¥131,495 |
JR東海 | 1 | 0.1 | 0.5603272 | ¥131,495 |
キーエンス | 10 | 0.1 | 0.5214724 | ¥129,940 |
伊藤忠 | 1 | 0.1 | 0.5316973 | ¥129,484 |
ファナック | 1 | 0.1 | 0.5276074 | ¥126,624 |
アステラス | 10 | 0.1 | 0.5194274 | ¥124,628 |
武田 | 1 | 0.1 | 0.4989775 | ¥124,192 |
積ハウス | 1 | 0.1 | 0.5071575 | ¥123,826 |
豊田織 | 10 | 0.1 | 0.5276074 | ¥122,900 |
キヤノン | 1 | 1 | 0.5214724 | ¥121,812 |
まとめ
サポートベクターマシンとRSIの組み合わせでシミュレーションを作成しましたが、今回の組み合わせではマイナスの結果が4割弱存在することとなりました。閾値や学習期間の設定などのバリエーションを増やして分析を繰り返すべきではありますが、本モデルは計算時間を要するためある程度予測のもとパラメータ決定を行うことが重要そうです。