いろいろな株の売買の方法が提案されています。
例えば、以下はある本で紹介されていたものを単純化したものです。
買いのタイミング
株価の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でした。
うーん、やはり簡単にはいきませんね。
しかし、株は、売買のアルゴリズムを過去の実データで簡単に検証できるののがとても良いですね。
コメントを残す