TimesFMをDockerで使って、学習ゼロで株価予測する

はじめに

前回、TimesFMを使ってsin波を予測しました。

sinの単調波では、見事に学習せずに予測が可能でしたが、合成波となると正しい予測はできませんでした。

しかし、ピークや折り返しなどの周波数特性は正しく予期できていたようにも感じます。

そこで、今回は株価に対してTimesFMを使ってみて、ピークや折り返し等が正しく予測できるのかを確かめたいと思います。

環境構築等は前回の記事をご確認ください。
※論文を確認せずにノリでパラメータを弄っているので、適切なパラメータを設定できているのか不明です。
※timesFMの学習データに株価も入っていたと思うので、apple株が学習されていた場合には、学習データをテストデータに利用したということになります。そのため、この記事での話は、そんなに信頼しないでください

実験 Apple株 1日足

以下jupyter notebookの記録です

まず、timesfmやyfinanceをインポートします。

import timesfm
import yfinance as yf

Appleの株価をダウンロードします。

df = yf.download(tickers='AAPL', interval='1d')
df = df.reset_index().rename(columns={'Datetime':'ds'})
df['unique_id'] = 'APPL'
df

yfinanceが問題なければ、数秒でapple株の株価の記録が取得できます。
1980年からの株価が取れました。

[*********************100%%**********************]  1 of 1 completed
Date	Open	High	Low	Close	Adj Close	Volume	unique_id
0	1980-12-12	0.128348	0.128906	0.128348	0.128348	0.099058	469033600	APPL
1	1980-12-15	0.122210	0.122210	0.121652	0.121652	0.093890	175884800	APPL
2	1980-12-16	0.113281	0.113281	0.112723	0.112723	0.086998	105728000	APPL
3	1980-12-17	0.115513	0.116071	0.115513	0.115513	0.089152	86441600	APPL
4	1980-12-18	0.118862	0.119420	0.118862	0.118862	0.091737	73449600	APPL
...	...	...	...	...	...	...	...	...
10944	2024-05-13	185.440002	187.100006	184.619995	186.279999	186.279999	72044800	APPL
10945	2024-05-14	187.509995	188.300003	186.289993	187.429993	187.429993	52393600	APPL
10946	2024-05-15	187.910004	190.649994	187.369995	189.720001	189.720001	70400000	APPL
10947	2024-05-16	190.470001	191.100006	189.660004	189.839996	189.839996	52845200	APPL
10948	2024-05-17	189.509995	190.809998	189.179993	189.869995	189.869995	41260800	APPL
10949 rows × 8 columns

データが多すぎると感じたため、2015年以前は切り捨てます。また、モデルにいれるのは2024年までのデータとし、それ以降の推移を予測値と実際の値を比較します。

import pandas as pd
df['ds'] = pd.to_datetime(df['ds'])
input_df = df.loc[('2015-01-01' <= df['ds']) & (df['ds'] <= '2023-01-01'), :]
input_df
ds	Open	High	Low	Close	Adj Close	Volume	unique_id
8589	2015-01-02	27.847500	27.860001	26.837500	27.332500	24.402172	212818400	APPL
8590	2015-01-05	27.072500	27.162500	26.352501	26.562500	23.714727	257142000	APPL
8591	2015-01-06	26.635000	26.857500	26.157499	26.565001	23.716957	263188400	APPL
8592	2015-01-07	26.799999	27.049999	26.674999	26.937500	24.049519	160423600	APPL
8593	2015-01-08	27.307501	28.037500	27.174999	27.972500	24.973555	237458000	APPL
...	...	...	...	...	...	...	...	...
10848	2023-12-22	195.179993	195.410004	192.970001	193.600006	193.091385	37122800	APPL
10849	2023-12-26	193.610001	193.889999	192.830002	193.050003	192.542816	28919300	APPL
10850	2023-12-27	192.490005	193.500000	191.089996	193.149994	192.642548	48087700	APPL
10851	2023-12-28	194.139999	194.660004	193.169998	193.580002	193.071426	34049900	APPL
10852	2023-12-29	193.899994	194.399994	191.729996	192.529999	192.024185	42628800	APPL
2264 rows × 8 columns

timesFMのモデルを作ります。入力長は512日、出力長は128日としました

tfm = timesfm.TimesFm(
    context_len=512,
    horizon_len=128,
    input_patch_len=32,
    output_patch_len=128,
    num_layers=20,
    model_dims=1280,
    backend="cpu",
)

huggingfaceからモデルの学習済モデルの重みをロードします

import json
with open('/work/api_keys.json', 'r') as f:
    api_keys = json.load(f)
from huggingface_hub import login
login(token = api_keys['huggingface_hub'])
tfm.load_from_checkpoint(repo_id="google/timesfm-1.0-200m")

timesFMによる予測をします。入力がDataFrameのときは、forecastではなく、forecast_on_dfを利用できます。

forecast_df = tfm.forecast_on_df(
    inputs=input_df,
    freq="D", 
    value_name="Close",
    num_jobs=-1,
)
forecast_df

予測値のDataFrameを見てみます

	unique_id	ds	timesfm	timesfm-q-0.1	timesfm-q-0.2	timesfm-q-0.3	timesfm-q-0.4	timesfm-q-0.5	timesfm-q-0.6	timesfm-q-0.7	timesfm-q-0.8	timesfm-q-0.9
0	APPL	2023-12-30	193.824905	189.920166	191.067902	192.396881	193.090027	193.824905	194.257050	194.873138	195.946579	196.972046
1	APPL	2023-12-31	193.135178	187.809860	189.859909	191.212402	192.315063	193.135178	193.971436	195.005432	196.001022	197.756500
2	APPL	2024-01-01	193.152405	186.541275	189.248810	190.769104	192.029007	193.152405	194.231079	195.556320	196.747528	199.074417
3	APPL	2024-01-02	192.817001	185.113586	187.716446	189.817917	191.249908	192.817001	194.021362	195.412933	197.100113	199.621582
4	APPL	2024-01-03	192.280167	184.058487	186.997787	189.262711	190.785080	192.280167	193.764771	195.414093	197.317627	199.956375
...	...	...	...	...	...	...	...	...	...	...	...	...
123	APPL	2024-05-01	192.947479	159.177216	171.322449	179.525940	186.530045	192.947479	199.136429	206.269943	214.595337	227.278061
124	APPL	2024-05-02	193.503845	159.004486	171.467606	179.526306	186.967072	193.503845	199.708710	206.847366	215.365814	228.087616
125	APPL	2024-05-03	193.618210	158.960068	171.360428	179.962936	187.098495	193.618210	199.996384	207.489380	215.808807	228.819122
126	APPL	2024-05-04	193.407379	158.507553	171.073883	179.440750	186.996185	193.407379	200.238815	207.045792	215.834259	229.334900
127	APPL	2024-05-05	193.601639	157.930084	170.636337	179.551376	186.643951	193.601639	200.288055	207.499008	216.571320	230.005173
128 rows × 12 columns

予測結果を描画してみます

import matplotlib.pyplot as plt

plot_df = df.loc[df['ds'] > '2022-01-01']
plt.plot(plot_df['ds'], plot_df['Close'], label='raw')
plt.plot(forecast_df['ds'], forecast_df['timesfm'], label='predict')
plt.fill_between(forecast_df['ds'], forecast_df["timesfm-q-0.4"], forecast_df["timesfm-q-0.6"], color='gray', alpha=0.3, label='predict-bounds')

plt.xticks(rotation=30)
plt.legend()

あまり、予測できたとは言いづらい結果になりました。
しかし、下がって、上がって下がって上がるという傾向は確かに合っています。これは偶然でしょうか?

2023年からの予測

2023-01-01から予測してみます

import pandas as pd
df['ds'] = pd.to_datetime(df['ds'])
input_df = df.loc[('2015-01-01' <= df['ds']) & (df['ds'] <= '2023-01-01'), :]
forecast_df = tfm.forecast_on_df(
    inputs=input_df,
    freq="D",
    value_name="Close",
    num_jobs=-1,
)
import matplotlib.pyplot as plt

plot_df = df.loc[df['ds'] > '2022-01-01']
plt.plot(plot_df['ds'], plot_df['Close'], label='raw')
plt.plot(forecast_df['ds'], forecast_df['timesfm'], label='predict')
plt.fill_between(forecast_df['ds'], forecast_df["timesfm-q-0.4"], forecast_df["timesfm-q-0.6"], color='gray', alpha=0.3, label='predict-bounds')

plt.xticks(rotation=30)
plt.legend()

株価自体はまたしても、全く一致しませんが、トレンドの上下および傾向はなんとなくあっているような気もします。

2022年からの予測

2022-01-01から予測してみます

import pandas as pd
df['ds'] = pd.to_datetime(df['ds'])
input_df = df.loc[('2015-01-01' <= df['ds']) & (df['ds'] <= '2022-01-01'), :]
forecast_df = tfm.forecast_on_df(
    inputs=input_df,
    freq="D",
    value_name="Close",
    num_jobs=-1,
)
import matplotlib.pyplot as plt

plot_df = df.loc[df['ds'] > '2022-01-01']
plt.plot(plot_df['ds'], plot_df['Close'], label='raw')
plt.plot(forecast_df['ds'], forecast_df['timesfm'], label='predict')
plt.fill_between(forecast_df['ds'], forecast_df["timesfm-q-0.4"], forecast_df["timesfm-q-0.6"], color='gray', alpha=0.3, label='predict-bounds')

plt.xticks(rotation=30)
plt.legend()

全く見当違いな予測をしました。2023-01-01と2024-01-01からの予測で、傾向があっていたのは偶然だったかもしれません。

長期間の予測

予測期間を長くしてみて、当たるのかを試してみます。なんとなく512日間を予測期間としてみます。

tfm = timesfm.TimesFm(
    context_len=1024,
    horizon_len=512,
    input_patch_len=32,
    output_patch_len=128,
    num_layers=20,
    model_dims=1280,
    backend="cpu",
)

tfm.load_from_checkpoint(repo_id="google/timesfm-1.0-200m")

import pandas as pd
df['ds'] = pd.to_datetime(df['ds'])
input_df = df.loc[('2015-01-01' <= df['ds']) & (df['ds'] <= '2023-01-01'), :]
forecast_df = tfm.forecast_on_df(
    inputs=input_df,
    freq="D",
    value_name="Close",
    num_jobs=-1,
)
import matplotlib.pyplot as plt

plot_df = df.loc[df['ds'] > '2022-01-01']
plt.plot(plot_df['ds'], plot_df['Close'], label='raw')
plt.plot(forecast_df['ds'], forecast_df['timesfm'], label='predict')
plt.fill_between(forecast_df['ds'], forecast_df["timesfm-q-0.4"], forecast_df["timesfm-q-0.6"], color='gray', alpha=0.3, label='predict-bounds')

plt.xticks(rotation=30)
plt.legend()

予想通り、長期間(1年と半年程度)の予測はできませんでした。未来視でも持っていない限り、アップルの株価が1年後どうなっているかなんて、正確に予想することが叶うわけがありません。

もう少し企業の成長能力が反映されづらい、為替や石油価格等の予測ではもしかしたら、うまくいくのかもしれません。

続きを書く気が湧いたら、為替の予測もしてみたいと思います

おわりに

株価の予測はできるとは言いづらい結果になりました。
一部の期間で、トレンドが一致している可能性が示唆されましたが、多くの期間で検証実験をしていないため、「予測できる」という結論を今回は導けませんでした。

追記

timesFMで為替予測する話を書きました。

タイトルとURLをコピーしました