2025-11-13

自動売買への道 (2025-11-13)

楽天証券の口座でデイトレの自動売買に挑戦しようと Windows / Excel 上で利用できる マーケットスピード II RSS を活用して Python であれこれ取り組んでいます。この「自動売買への道」のトピックでは、プログラミングの話題にも踏み込んで、日々の活動をまとめていきます。

今日の日経平均株価

現在値 51,281.83 +218.52 +0.43% 15:30
前日終値 51,063.31 11/12 高値 51,338.89 10:18
始値 51,013.15 09:00 安値 50,954.27 09:33

※ 右の 15 分足チャートは Yahoo! Finance のデータを yfinance で取得して作成しました。

【関連ニュース】

デイトレ用自作アプリ

以下は株価に関連する情報の流れを示しています。

株価データの流れ(Windows 11)

楽天証券では、Python からネットワーク越しに直接取引できるような API が提供されていないので、マーケットスピード II RSS を介して取引をする構成を取っています。

現在は、強化学習を利用した取引モデルの開発に取り組んでいます。

強化学習システムの概要

取引回数の制限

前回のマラソンテストで、取引回数が多いほど損失額が大きくなっているという傾向が見られたので、さしあたって最大取引回数の上限を 100 回とし、上限を超えたら取引を終了するようにしました。

観測値にも取引回数のカウントダウン情報を追加しました。「取引した回数」ではなくて「残りの取引回数」にした理由は、生成 AI (Microsoft Copilot) のアドバイス(下記)を採用したからです。

  • 「どこが下限かを明確にする」目的なら、カウントダウン方式(例:残り回数を直接表す)が最も直感的で、エージェントにも明確な時間的制約を伝えられます。

さらに、生成 AI が提案した対数スケーリングを採用して、残り回数が少なくなるほど、観測値の変化が急になるようにしました。

実装例
import numpy as np

def log_scaled_remaining(trade_counter, max_trade_count):
    return np.log1p(trade_counter) / np.log1p(max_trade_count)
  • trade_counter = 100 → obs ≈ 1.0
  • trade_counter =  10 → obs ≈ 0.65
  • trade_counter =   1 → obs ≈ 0.3
  • trade_counter =   0 → obs = 0.0

動作確認用に過去のティックデータ 1 ファイルで学習して、別のティックデータ 1 ファイルに対して推論してみました。

推論に使用したティックデータ

下の観測値の検証用トレンドチャートの、下から 4 番目の TRADE という観測値が取引回数のカウントダウンです。

取引回数の上限を設定した時の観測値のトレンド

推論の実行例

過去データ ticks_20251009.xlsx の銘柄 7011 について推論します。
モデル models/ppo_7011.zip を読み込みます。
Wrapping the env with a `Monitor` wrapper
Wrapping the env in a DummyVecEnv.
                   注文日時 銘柄コード  売買  約定単価  約定数量    損益
0   2025-10-09 09:01:10  7011  売建  4320     1   NaN
1   2025-10-09 09:01:15  7011  買埋  4336     1 -16.0
2   2025-10-09 09:01:17  7011  買建  4330     1   NaN
3   2025-10-09 09:01:18  7011  売埋  4329     1  -1.0
4   2025-10-09 09:01:19  7011  売建  4329     1   NaN
..                  ...   ...  ..   ...   ...   ...
95  2025-10-09 11:06:14  7011  買埋  4247     1  -1.0
96  2025-10-09 11:06:17  7011  売建  4246     1   NaN
97  2025-10-09 11:06:42  7011  買埋  4247     1  -1.0
98  2025-10-09 11:06:43  7011  買建  4247     1   NaN
99  2025-10-09 11:06:44  7011  売埋  4247     1   0.0

[100 rows x 6 columns]
一株当りの損益 : 64.0 円
モデルへの報酬分布
n: 7557, mean: 0.990, stdev: 0.721
観測数 : 11

マラソンテストのパフォーマンス

取引回数の上限の設定が機能することを確認できたので、この状態で、過去データ全てで学習をさせて、その学習モデルで同じ過去データを推論させました。

すると、今回は取引回数が全て上限の 100 回になってしまいました。😵‍💫

学習済みモデルで昨日までの過去データを推論[収益は 1 株単位]

以下は、上のプロットを添付した上でこの現象に関して、生成 AI (Microsoft Copilot) とやりとりをした内容です。なお、生成 AI の余計なお世辞は省いてあります。

なお、現時点ではまだ取引回数に対してペナルティを付与していません。

あらためて過去の複数のティックデータ (58) で学習させて、同じティックデータで順番に推論させて、収益と取引回数の関係を調べました。 全て取引回数が 100 回になってしまいました。取引回数に対して報酬やペナルティが無いと、こういった変化が顕著な観測値をモデルは選好しやすくなるのかな?

はい、取引回数に対する報酬やペナルティが無い場合、モデルは「変化のある観測値」に反応しやすくなり、結果として頻繁な取引を選好する傾向が強まります。

🔍 なぜ取引回数が最大化されるのか?

  1. 報酬関数が「変化に反応する」構造になっている
    • 多くのRL報酬設計では、ポジション変化による損益(ΔPL)が報酬の中心
    • 変化があるほど報酬が発生する → 変化を起こす行動(BUY/SELL)を好む
  2. HOLDには報酬がない/少ない
    • HOLDを続けても報酬が得られないなら、モデルは「何かしらのアクションを起こす」ことで報酬を得ようとする
    • その結果、観測値の変化に対して反射的に取引を繰り返すようになる
  3. Maskable PPO の構造的特性
    • マスクされていない限り、行動空間の中で報酬期待値が高いものを選ぶ
    • 取引コストが無いなら、BUY/SELLの期待値が常に高くなりやすい

 

念の為、前出と同じ過去のティックデータで推論させ、観測値を確認しました。

学習を重ねたモデルでは、かなり早いタイミングで取引回数 100 回の上限に達するようになりました。

取引回数の上限を設定した時の観測値のトレンド (2)

推論の実行例 (2)

過去データ ticks_20251009.xlsx の銘柄 7011 について推論します。
モデル models/ppo_7011.zip を読み込みます。
Wrapping the env with a `Monitor` wrapper
Wrapping the env in a DummyVecEnv.
                   注文日時 銘柄コード  売買  約定単価  約定数量   損益
0   2025-10-09 09:01:10  7011  買建  4320     1  NaN
1   2025-10-09 09:01:11  7011  売埋  4320     1  0.0
2   2025-10-09 09:01:12  7011  買建  4321     1  NaN
3   2025-10-09 09:01:13  7011  売埋  4321     1  0.0
4   2025-10-09 09:01:14  7011  買建  4336     1  NaN
..                  ...   ...  ..   ...   ...  ...
95  2025-10-09 09:02:46  7011  買埋  4302     1 -7.0
96  2025-10-09 09:02:47  7011  売建  4302     1  NaN
97  2025-10-09 09:02:48  7011  買埋  4306     1 -4.0
98  2025-10-09 09:02:49  7011  売建  4306     1  NaN
99  2025-10-09 09:02:50  7011  買埋  4307     1 -1.0

[100 rows x 6 columns]
一株当りの損益 : -10.0 円
モデルへの報酬分布
n: 160, mean: -0.001, stdev: 0.031
観測数 : 11

報酬関数に取引コストペナルティを導入

比較用のベースラインを確認できたので、報酬関数に下記のような取引コストペナルティを導入しました。

# 取引コストペナルティ付与
reward -= (n_trade_max - n_trade_remain) * penalty_trade_count

BUY / SELL のアクションを取る度にペナルティを付与します。

ただし、

  • n_trade_max = 100  # 取引回数の最大値
  • n_trade_remain  # BUY/SELL の度にカウントダウンされる残り取引回数
  • penalty_trade_count = 0.005  # 回数が増えるたびにカウントに乗ずる係数(仮決め)

です。

動作確認用に過去のティックデータ 1 ファイルで学習して、別のティックデータ 1 ファイルに対して推論してみました。

繰り返しになりますが、下から 4 番目の TRADE という観測値が取引回数のカウントダウンです。

取引コストペナルティを導入した時の観測値のトレンド (1)

推論の実行例

過去データ ticks_20251009.xlsx の銘柄 7011 について推論します。
モデル models/ppo_7011.zip を読み込みます。
Wrapping the env with a `Monitor` wrapper
Wrapping the env in a DummyVecEnv.
                  注文日時 銘柄コード        売買  約定単価  約定数量    損益
0  2025-10-09 09:02:00  7011        売建  4290     1   NaN
1  2025-10-09 10:53:36  7011        買埋  4250     1  40.0
2  2025-10-09 10:54:45  7011        売建  4254     1   NaN
3  2025-10-09 15:24:59  7011  買埋(強制返済)  4203     1  51.0
一株当りの損益 : 91.0 円
モデルへの報酬分布
n: 19391, mean: 1.333, stdev: 0.678
観測数 : 11

ペナルティが効きすぎて、最後まで建玉を返済しない傾向が強くなっているように見えますが、そもそも「取引回数が多い場合は損失が増える」というケースを減らすのが目的なので、この条件でマラソンテストにかけてみます。

マラソンテストのパフォーマンス (2)

再び、過去データ全てで学習をさせて、その学習モデルで同じ過去データを推論させました。

学習済みモデルで昨日までの過去データを推論[収益は 1 株単位](2)

う〜ん、若干の改善は見られるものの、依然として取引回数は 100 回にほぼ貼り付いています。

前出と同じ過去のティックデータで推論させて観測値を確認すると、比較的早い段階で取引回数の上限近くに達して。その後はペナルティの大きさからかエントリしていません。

取引回数に関わる報酬関数について、他も試して見る必要がありそうです。

参考サイト

  1. マーケットスピード II RSS | 楽天証券のトレーディングツール
  2. マーケットスピード II RSS 関数マニュアル
  3. 注文 | マーケットスピード II RSS オンラインヘルプ | 楽天証券のトレーディングツール
  4. Gymnasium Documentation
  5. Stable-Baselines3 Docs - Reliable Reinforcement Learning Implementations
  6. Maskable PPO — Stable Baselines3 - documentation
  7. PyTorch documentation
  8. PythonでGUIを設計 | Qtの公式Pythonバインディング
  9. Python in Excel alternative: Open. Self-hosted. No limits.
  10. Book - xlwings Documentation
にほんブログ村 株ブログ 株日記へ
PVアクセスランキング にほんブログ村

0 件のコメント:

コメントを投稿