いろいろな株の売買の方法が提案されています。
例えば、以下はある本で紹介されていたものを単純化したものです。
買いのタイミング
株価の75日移動平均線が上を向いていて、株価が75日移動平均線を下から上に抜けるとき。売りのタイミング
株価が75日移動平均線を上から下に抜けるとき。
このブログは、上の方法を東証COREの30銘柄のデータすべてに試してみたときの覚書になります。
コードは、Pythonで、Jupyter notebook で順番に実行していくという想定です。
移動平均を求める関数を作る
75日移動平均線とは、今から過去75日までの株価の平均を、「今」の時間でプロットして作られる線です。
以下は、日数を引数として移動平均線を求める関数を作り、ChatGPTにリファクタリングしてもらってできたコードです。作る過程はこちらのブログに詳細を書きました。
def moving_average(price_series, n):
"""
移動平均を求める関数。
Args:
price_series (List[float]): 価格時系列データ。
n (int): 平均化する区間の幅。
Returns:
List[float]: 移動平均。
Examples:
>>> dat = [0, 1, 2, 3, 4, 5]
>>> ma = moving_average(dat, n=3)
>>> print(ma)
[None, None, 1.0, 2.0, 3.0, 4.0]
"""
moving_avg = [None] * (n - 1)
for i in range(n - 1, len(price_series)):
avg = sum(price_series[i - n + 1:i + 1]) / n
moving_avg.append(avg)
return moving_avg
株価の人工データを生成する関数を作る
はじめから実データを使うより、小さい人工データを作ってプログラムを確かめながら進めた方が効率的ですので、人工データ生成関数を作りました。
import matplotlib.pyplot as plt
import numpy as np
def generate_price_series(initial_price: float, num_days: int, std_days: float, seed: int = 0) -> list:
"""
指定された初期価格、日数、1日あたりの変動の標準偏差に基づいて、株価データを生成する関数。
Parameters
----------
initial_price: float
株価の初期値。
num_days: int
株価データを生成する日数。
std_days: float
株価の1日あたりの変動の標準偏差。
seed: int, optional (default=0)
乱数生成器のシード値。
Returns
-------
price_series: list
生成された株価データのリスト。
"""
# 乱数生成器のシード値を設定
np.random.seed(seed)
# 日毎の価格変動を表す乱数を生成
price_series = [initial_price]
for i in range(1, num_days):
# 標準正規分布に従う乱数を生成し、1日あたりの変動の標準偏差を掛けて株価を決定
price_today = price_series[-1] * (1 + np.random.randn() * std_days)
price_series.append(price_today)
return price_series
# テスト -----
# データの生成
initial_price = 1000 # 初期価格
num_days = 250 # 日数
std_days = 0.1 # 1日の変動の標準偏差
seed = 0 # 乱数生成器のシード値
price_series = generate_price_series(initial_price, num_days, std_days, seed)
# グラフの描画
plt.plot(price_series)
plt.title("株価の変動", fontname="MS Gothic")
plt.xlabel("日数", fontname="MS Gothic")
plt.ylabel("価格", fontname="MS Gothic")
plt.show()
ランダムウォークを仮定して作っています。いい感じですね。
株価売買アルゴリズムを作る
では、株価売買アルゴリズムを実装していきます。
小さい人工データを作る
まず、人工データを準備します。
# 株価の人工データを作る ----- initial_price = 1000 # 初期価格 num_days = 20 # 日数 std_days = 0.1 # 1日の変動の標準偏差 seed = 5 # 乱数生成器のシード値 v = generate_price_series(initial_price, num_days, std_days, seed) v = np.array(v) ts = np.array(range(len(v))) # 確認 plt.plot(ts, v, '.-'); plt.show()
2日間の移動平均を求めて、先ほどのグラフに重ねる
実際には75日間の移動平均を使いますが、プログラムが正しいことを確かめやすいように、まず2日間の移動平均でアルゴリズムを作ります。
# n日移動平均を作る ----
num_average = 2
ml = moving_average(v, n=num_average)
# 確認
plt.plot(ts, v, '.-', label='data')
plt.plot(ts, ml, '.-', label=f'average {num_average}')
plt.legend();
今の株価と一つ前の日の株価の平均が、正しくオレンジのグラフで表されていることがわかります。
メインの売買シミュレーションを作る
では、アルゴリズムの実装です。ルールを再掲します。
買いのタイミング
株価の75日移動平均線が上を向いていて、株価が75日移動平均線を下から上に抜けるとき。
売りのタイミング
株価が75日移動平均線を上から下に抜けるとき。
# 売買シミュレーション -----
# 状態を表す変数
stock = 0 # 0:株を保持していない、1)保持している
# 記録用変数
wallets = [0] # 資産
t_wallets = [0] # 資産が変化したときの日(表示用)
t_sells = [] # 株を買った日(表示用)
t_buys = [] # 株を売った日(表示用)
# シミュレーション
for t in range(1, len(ml)):
if ml[t-1] is None:
continue
if stock == 0:
# 株を持っていない時
if ml[t-1] < ml[t] and v[t-1] < ml[t-1] and ml[t] < v[t]:
# mlが上昇中でかつ、 vがmlを下から上に抜けた時に買い
t_buys.append(t)
debt = v[t]
stock = 1
else:
# 株を持っている時
if v[t] < ml[t]:
# vがmlを下回ったら 売り
t_sells.append(t)
wallets.append(wallets[-1]+v[t]-debt)
t_wallets.append(t)
debt = 0
stock = 0
# 出力のまとめ
out = {
"wallets": wallets,
"t_wallets": t_wallets,
"t_sells": t_sells,
"t_buys": t_buys,
}
out
以下のように出力されます。
{'wallets': [0, -31.63707397727876, -208.8477441428047, -202.82313182644646],
't_wallets': [0, 4, 15, 19],
't_sells': [4, 15, 19],
't_buys': [3, 14, 17]}
結果をグラフ表示します。
# グラフ表示
plt.figure(figsize=(10, 6))
plt.subplots_adjust(hspace=0.6)
# 株価の変化
ax = plt.subplot(2,1,1)
plt.plot(ts, v, marker=".")
plt.plot(ts, ml, marker=".")
for t0, t1 in zip(t_buys, t_sells):
plt.plot([ts[t0], ts[t1]], [v[t0], v[t1]], '-y', linewidth=5)
plt.plot(ts[t_buys], v[t_buys], 'or')
plt.plot(ts[t_sells], v[t_sells], 'sb')
plt.grid()
xrange = ax.get_xlim()
# 資産の変化
plt.subplot(2,1,2)
plt.step(ts[t_wallets], wallets, '.-', where="post")
plt.xlim(xrange)
plt.grid()
plt.title("wallets")
plt.show()
株価(青)が平均線(オレンジ)を越えた時に、株を買い(赤丸)、株価が平均線よりも下がったら株を売ります(青)。
株を売った時に、買った時との差額でお財布(Wallets)の中身が変わります。
赤と青を結んだ黄色の線が上向きだと儲けが出て、下向きだと損をしていることになります。この例ですと、3回の取引をしていて、初めの2回で損をして、3回目に少し得をするという結果になっています。
関数化する
ここまでの処理を関数にして、複数のデータを一度に処理する準備をします。
def sim_trade(v, num_average, f_show=True):
v = np.array(v)
ts = np.array(range(len(v)))
# 75日移動平均を作る ----
ml = moving_average(v, n=num_average)
# 売買シミュレーション -----
# 状態を表す変数
stock = 0 # 0:株を保持していない、1)保持している
# 記録用変数
wallets = [0] # 資産
t_wallets = [0] # 資産が変化したときの日(表示用)
t_sells = [] # 株を買った日(表示用)
t_buys = [] # 株を売った日(表示用)
# シミュレーション
for t in range(1, len(ml)):
if ml[t-1] is None:
continue
if stock == 0:
# 株を持っていない時
if ml[t-1] < ml[t] and v[t-1] < ml[t-1] and ml[t] < v[t]:
# mlが上昇中でかつ、 vがmlを下から上に抜けた時に買い
t_buys.append(t)
debt = v[t]
stock = 1
else:
# 株を持っている時
if v[t] < ml[t]:
# vがmlを下回ったら 売り
t_sells.append(t)
wallets.append(wallets[-1]+v[t]-debt)
t_wallets.append(t)
debt = 0
stock = 0
# 出力のまとめ
out = {
"wallets": wallets,
"t_wallets": t_wallets,
"t_sells": t_sells,
"t_buys": t_buys,
}
# グラフ表示
if f_show:
# グラフ表示
plt.subplots_adjust(hspace=0.6)
# 株価の変化
ax = plt.subplot(2,1,1)
plt.plot(ts, v, marker=".")
plt.plot(ts, ml, marker=".")
for t0, t1 in zip(t_buys, t_sells):
plt.plot([ts[t0], ts[t1]], [v[t0], v[t1]], '-y', linewidth=5)
plt.plot(ts[t_buys], v[t_buys], 'or')
plt.plot(ts[t_sells], v[t_sells], 'sb')
plt.grid()
plt.title("株価の変動", fontname="MS Gothic")
xrange = ax.get_xlim()
# 資産の変化
plt.subplot(2,1,2)
plt.step(ts[t_wallets], wallets, '.-', where="post")
plt.xlim(xrange)
plt.grid()
plt.title("お財布", fontname="MS Gothic")
return out
# 株価の人工データを作る -----
initial_price = 1000 # 初期価格
num_days = 20 # 日数
std_days = 0.1 # 1日の変動の標準偏差
seed = 5 # 乱数生成器のシード値
v = generate_price_series(initial_price, num_days, std_days, seed)
# テスト ----
num_average = 2
out =sim_trade(v, num_average, f_show=True)
実際の株価のデータでテストする
前のブログで作った東証Coreの30銘柄データをまとめたall_close.xls を使います。
株価データの読み込み
銘柄の一つ"c5"(リクルートホールディングス)に対して、売買のアルゴリズムを試してみます。
c5 (リクルートホールディングス)のデータに、実際の手順で指示されている75日移動平均を使いました。売買のチャンスはめったにこないですね。この場合ですと、200日の間に、買って売れたのは1回だけ、そこで損をしています。
30銘柄で試す
では、30銘柄のデータすべてに同じアルゴリズムで売買してみて、どれくらい稼げるのかを見てみます。
上のグラフの各プロットが、各30銘柄の結果になります。横軸が稼いだ金額、縦軸が売買をした回数です。
儲けが出ている銘柄もありますが、多くは損をしています。平均が-39.22、中央値が-20.15でした。
うーん、やはり簡単にはいきませんね。
しかし、株は、売買のアルゴリズムを過去の実データで簡単に検証できるののがとても良いですね。






コメントを残す