
知っておくべきMQL5ウィザードのテクニック(第43回):SARSAによる強化学習
はじめに
強化学習(RL)を利用することで、取引システムは環境や市場データから学習し、時間の経過とともに取引能力を向上させることがRLは変化する市場環境に適応できるため、特定のダイナミックな金融市場や証券に適しています。金融市場は予測不可能であり、しばしば不確実性が高いです。RLは、受け取ったフィードバック(報酬)に基づいて行動を継続的に調整することで、不確実性の下で意思決定をおこなうことに優れているため、不安定な市場環境に対処する際にトレーダーに非常に役立ちます。
これに類似する例として、チャートに接続され、直近の価格履歴をもとに定期的に自己最適化をおこない、パラメータを微調整するEAが挙げられます。RLの目的は、同じことを、派手さなしにおこなうことです。RLをここまで見てきた本連載では、厳密な定義の意味で、(教師あり学習、教師なし学習に加えて)機械学習における学習の第三のアプローチとしてRLを使用してきました。予測に使える独立したモデルとしてはまだ見ていません。
この記事ではそれが変わります。単に異なるRLアルゴリズムであるSARSA法を導入するだけでなく、これを独立したシグナルモデルとして、ウィザードで組み立てられたEAの別のカスタムシグナルクラス内に実装することを目指します。シグナルモデルとして使用される場合、RLは意思決定のプロセスを自動化し、常に人間が介入する必要性を減らすことができるため、(少なくとも理論上は)高頻度取引や市場の動きへのリアルタイム対応が可能になります。らに、報酬メカニズムを通じた継続的なフィードバックによって、RLモデルはリスク管理の能力を向上させる傾向があります。これは、リスクの高い行動には低い報酬でペナルティを与えることで実現され、この正味の効果は、RLがボラティリティの高い取引や損失を出す取引へのエクスポージャーを最小限に抑えることです。
RLは基本的に、探査(exploration)と活用(exploitation)のバランスを取ること、すなわち新しい戦略を試すことと、すでに利益を上げている戦略を使うことのバランスを取ることでもあります。これは、Qマップを更新する際の「ε-greedy」アプローチによって可能になりました。Qマップは、エージェントが行動を選択するための、可能な環境状態全体にわたる行動の行列です。
RLの急速な進化とAIにおける役割を考慮すると、経験則的な利点である可能性があるものの、RLには他にも注目すべき利点があります。
過去のデータとリアルタイムのフィードバックに基づき、売買の最適なタイミングと価格ポイントを学習することで、取引の執行を最適化し、収益性を向上させることができます。これは、この記事のケースのように、基本的なシグナルモデルとして、別のシグナルと組み合わせることで、未決注文が使用されている状況で、その役割がどのように決定されるかを決定することによって達成されます。たとえば、Qマップに指値、逆指値、成行注文の3つの行動があれば、すでに確立された馴染みのある戦略のエントリーポイントを微調整することができます。
RLは従来の線形モデルと異なり、少し型破りですが、現実世界の市場行動をより反映している複雑で非線形な取引戦略を学習して実行するのに適していると考えられます。また、RLは、Qマップとそれに付随する行動の定義次第で、複数の資産クラスや戦略を同時に扱うことができる拡張性を備えており、多様な金融商品にわたるポートフォリオ管理やアルゴリズム取引に対応できる汎用性の高い解決策となっています。最後に、リアルタイム意思決定システムに特に適しており、完全自動のEAだけでなく、戦略や現在のセットアップに応じて、一部の手動取引システムやEAを含むように拡張することも可能です。
SARSAの紹介
SARSAとは、State-Action-Reward-State-Actionの頭文字を取ったもので、Qマップの値がどのように更新されるかに由来します。このQ値の更新方法は、以前の記事で取り上げたQ学習アプローチとは明確に異なり、オフポリシーアプローチではなくオンポリシーアプローチを採用している点が特徴です。実際、この記事で紹介するSARSAの実装は、Qマップの値の更新方法を除けば、Q学習を紹介したその記事で使用したものと同じです。
SARSAのオンポリシーアルゴリズムは、現在の環境状態に基づいてこれから取る行動を選ぶのではなく、現在の方策に従ってすでに取った行動に基づいてQ値を学習することを意味します。Q値は、自分が従う同じ方策によって選択された行動を使用して更新されます。一方、Q学習はオフポリシーアルゴリズムであり、次の環境状態から最良の行動を選択することでQ値を更新します。現在の方策のとった行動に従う必要はなく、エージェントの現在の行動とは無関係に、最適な方策を学習します。このSARSAのQ値のオンポリシー更新を以下のように実装します。
//+------------------------------------------------------------------+ // Update Q-value using On-policy //+------------------------------------------------------------------+ void Cql::SetOnPolicy(double Reward, vector &E) { Action(E); //where 'act' index 1 represents the current Q-action from Q-Map double _action = Q[act[1]][e_row[0]][e_col[0]]; if(THIS.use_markov) { int _old_index = GetMarkov(e_row[1], e_col[1]); int _new_index = GetMarkov(e_row[0], e_col[0]); _action *= markov[_old_index][_new_index]; } for (int i = 0; i < THIS.actions; i++) { if(i == act[0]) { continue; } Q[i][e_row[1]][e_col[1]] += THIS.alpha * (Reward + (THIS.gamma * _action) - Q[act[0]][e_row[1]][e_col[1]]); } }
SARSAの更新ルールは、次の状態で実際に選択された行動を使用して更新をおこないます(状態→行動→報酬→状態→行動)。探索と学習については、ε-greedy方策に従います。これはQ学習で見た内容と非常に似ていますが、当時は明確でなかったものの、今回注目されたのは、Qマップの更新プロセスがランダム化されているため、ε-greedy選択が非常に不安定な結果をもたらす可能性があるという点です。原則として、εの値が小さいほどランダム効果は小さくなります。
探索と活用のバランスに関して言えば、SARSAはよりバランスの取れたアプローチを採用しています。なぜなら、学習と行動の両方において同じ方策に従うからです。これは理論上、一部の金融市場のような不確実な環境ではより安全であることを意味します。一方、Q学習は、常に次の状態から最大の報酬を得ようとするため、よりアグレッシブになる傾向があり、不安定な環境ではハイリスクな決断を下しやすくなる可能性があります。
したがって、SARSAは、探査と活用のバランスを維持することが重要であり、環境がリスキーまたは非定常であるシナリオに適しています。これに関して思い浮かぶ最良のシナリオは、たとえばいずれかのJPYペアを取引することです。逆に、Q学習は、環境が比較的安定しており、最適な戦略を見つけることが継続的なリスク管理よりも重要な場合に適しています。その最たる例がEUR CHFペアでしょう。
この記事では、もうひとつのRLアルゴリズムであるDeep-Q-Networksについても触れていますが、これもSARSAとは多くの点で異なります。主な違いは、SARSAがQ学習と同様に、状態行動の値を格納するためにQテーブルを使用する点です。このため、SARSAは状態空間が小さい環境に限定されます。大きな環境ではQテーブルを維持することが非現実的だからです。しかし、DQNは、その記事で見たように、ニューラルネットワークを利用して、各状態-動作のペアのQ値を近似するため、よりスケーラブルで、状態空間が大きい、あるいは連続的な環境において効果的です。
経験再生に関しては、SARSAは各経験から順番に学習するため、これを使用しません。これは、エージェントが直近の経験からしか学習しないため、非効率的な学習につながる可能性があります。しかしその一方で、DQNは過去の経験をバッファリングし、訓練中にランダムにサンプリングする経験再生を実装することができます。これは、連続した経験間の相関関係を減らすのに大いに役立ち、ひいてはより安定した学習につながる可能性があります。
SARSAとDQNのもうひとつの違いは、ターゲットネットワークの使用に起因します。SARSAにはそのような概念はなく、現在の方策に基づいて各ステップでQ値を直接更新します。一方、DQNの記事で見たように、学習を安定させるためにメインのQネットワークと並行してターゲットネットワークを使用することは必須です。DQNでは、ターゲットネットワークが定期的に更新されるため、Q値の更新が安定し、学習における大きな振動を防ぐことができます。
スケーラビリティと複雑さは、DQNとSARSAが異なるもうひとつの難解な点です。SARSAは、Qテーブルのサイズとオンポリシー学習の制限により、より小さく単純な問題に最適だからです。DQNは、画像ベースのタスクや膨大な数の状態を持つ環境で遭遇するような、より複雑で高次元の問題向けに設計されています。このSARSAの記事では、Q学習の記事と同じように、簡潔にするために環境状態を9つに限定しています。
要約すると、これらは強気、弱気、ウィップソー相場の3つの単純化された相場状態に基づいています。そして、これらの各状態を短い時間枠と長い時間枠に適用し、9つの可能な状態を意味する3×3の行列を作成します。経済ニュースデータや関連する証券の値動きなど、余分なパラメータを考慮する必要がある場合、このデータの連続的な値がSARSAの適用を制限する可能性があります。
最後に学習速度ですが、SARSAは逐次的な更新プロセスとニューラルネットワークの汎化の欠如により、複雑な環境では速度が遅くなる可能性があります。しかしDQNは、ニューラルネットワークを使って類似した状態を汎化する能力があるため、特に経験再生によるバッチ学習と組み合わせた場合、大規模な環境ではより高速になる傾向があります。
SARSA用MQL5環境のセットアップ
MLPや他の機械学習アルゴリズムではなく、RLをベースモデルとして使用するカスタムシグナルクラスを実装するには、要するに、入門記事で見たシグナルクラスを、Q学習に焦点を当てたRLに単純化する必要があります。この記事では、予測にMLPを使用し、RLは学習中の損失関数の処理に限定されています。当時のRLは、現在のSARSAと同様に、エージェントにとって最適な行動をランダムに選択するタイミングを導くε-greedyアプローチを採用しています。
RLを単にMLPの訓練プロセスのガイドとして使用していたときは、訓練から得られる損失値がMLPの全体的なパフォーマンスに対してそれほど敏感ではなかったため、このようなしばしばランダムな選択は「許容範囲内」でした。しかし、RLが他のMLPではなくモデルである今、ランダム行動の選択は全体的なパフォーマンスに不釣り合いな影響を与えます。
機械学習ではしばしば、訓練サンプルとテストサンプルがあります。習慣として、私はこれらのデータを提供しませんが、それについて言及し、読者にこれらの2つの独立したデータセットを入手するようお勧めします。通常、訓練データセットはテストデータセットよりもわずかに大きく、訓練とテストを分離するというこのプロトコルに従えば、εの使用はモデル全体にとって建設的なものとなります。Qマップは単なる行列配列であり、添付されたコード内ではどの関数からもエクスポートされていません。ただし、これは比較的簡単で、この先さらに独立した学習やテストをおこないたいと考える場合は、この行列配列を訓練後にシステム上でbinまたはCSVファイルとしてエクスポートし、テスト時に読み込む必要があります。
カスタムシグナルクラスのコーディング
私たちのカスタムシグナルクラスは、前述の通り、Q学習アルゴリズムの記事で紹介したものを簡略化したものであり、主な調整の1つは以下に示すように改訂した出力取得関数(GetOutput)です。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSignalSARSA::GetOutput(int &Output, Cql *QL) { vector _in, _in_row, _in_row_old, _in_col, _in_col_old; if ( _in_row.Init(m_scale) && _in_row.CopyRates(m_symbol.Name(), m_period, 8, 0, m_scale) && _in_row.Size() == m_scale && _in_row_old.Init(m_scale) && _in_row_old.CopyRates(m_symbol.Name(), m_period, 8, 1, m_scale) && _in_row_old.Size() == m_scale && _in_col.Init(m_scale) && _in_col.CopyRates(m_symbol.Name(), m_period, 8, 0, m_scale) && _in_col.Size() == m_scale && _in_col_old.Init(m_scale) && _in_col_old.CopyRates(m_symbol.Name(), m_period, 8, m_scale, m_scale) && _in_col_old.Size() == m_scale ) { _in_row -= _in_row_old; _in_col -= _in_col_old; vector _in_e; _in_e.Init(m_scale); QL.Environment(_in_row, _in_col, _in_e); int _row = 0, _col = 0; QL.SetMarkov(int(_in_e[m_scale - 1]), _row, _col); double _reward_float = _in_row[m_scale - 1]; double _reward_max = _in_row.Max(); double _reward_min = _in_row.Min(); double _reward = QL.GetReward(_reward_max, _reward_min, _reward_float); QL.SetOnPolicy(_reward, _in_e); Output = QL.transition_act; } }
このコードをEAに組み入れるには、新しい読者の方はこちらとこちらにあるガイドラインをご参照ください。今回の記事ではEAの使いやすさを実証するだけなので、組み立てられたEAは最適化されたε値でテストします。実運用においては、各環境状態とそれに対応する行動の相対的な重要性や意味を考慮した上で、適切な値を決定する必要があります。
SARSAベースのシグナルのテストとデバッグ
SARSAカスタムシグナルをよりよくテストし、活用するためには、このシグナルクラスにいくつかの変更を加える必要があります。おそらく最も重要なのは、Qマップ行列配列をエクスポートする関数を追加することでしょう。これにより、伝統的な意味での独立したテストと訓練が可能になります。フォワードウォークテストが利用できるため、これらの交差検証能力はストラテジーテスターに組み込まれています。
絶対的な価格水準、RSIやボリンジャーバンドの指標値などの代替的な市場状態を考慮し、これらの各データポイントの代替的な時間枠と交差させることによって、環境状態を定義した方法にも追加的な調整を加えることができます。 デバッグは、これらの状態変数がどのようにキャプチャされ、更新されているかを確認することから始めます。
報酬関数もまた、取引結果を正確に反映したものでなければなりません。この記事では、新しいバーごとに、価格帯のパーセンテージとして好ましいエクスカーションを使用します。これは、Qマップを同時に訓練し、その時点でのQ値の重み付けに基づいて取引を決定しているためです。 適切な独立したテストデータセットと訓練データセットを求めるべきであるからです。しかし、たとえば収益性や長期にわたるEAのパフォーマンスを考慮する場合、報酬の指標はここで使用しているものよりも長期的なものになります。
やみくもにEAの収益性を追い求めないためには、個々の状態-動作ペアをテストし、EAが期待通りに市場状況に正しく反応することを確認することも良いアイデアでしょう。たとえば、この記事の簡略版で示されているQマップの最初のグリッド座標「0,0」におけるQ値の重み付けを確認する場合、短期および長期の弱気の状態を示すこの座標では、行動0(売り)に対して最も高い重み付けがあるべきです。そして、それは弱気市場で取るべき行動と一致しない「カーブフィットされた値」ではないはずです。
ε-greedy方策によって実行される適切な探査と活用のバランスの検証も不可欠ですが、これは理想的なε値の最適化によって得ることができます。しかし、この最適化は、Qマップがより良いパフォーマンスを持つパスでバックアップされている別の訓練データセットで実行する必要があります。訓練の後、別のデータセットでテスト(フォワードウォーキング)をおこなうことで、使用したε値を確認または反証することができます。
バック訓練は、妥当な品質のデータセットでおこなうべきです。ストラテジーテスターレポートは、ストラテジーテスター合格後に使用されたデータの品質を示すものであり、訓練がどれだけ信頼できるものであったかを示す良い指標となります。この訓練は、テスト結果が以前のベンチマークよりも優れている場合、各パスの終了時にQマップをエクスポートします。このエクスポートされたQマップは、ニューラルネットワークを訓練する際のエポックとして機能するように、その後のバック訓練ラウンドで再利用されます。
訓練プロセスの最後に、最も満足のいくEAのパフォーマンスを提供する最終的なQマップは、この特定のQマップを使用した過去の訓練パフォーマンスが、まだ「未公開」のテストデータセットで再現できるかどうかを確認するために、1回のフォワードテストパスで使用されます。フォワードウォークはストラテジーテスターのネイティブ機能であり、この記事はこの機能を初めて使う読者へのガイドとなります。
過去のデータセットでの交差検証に加え、本格的な展開の前にライブ口座でも同じことが考えられます。 パフォーマンスロギングでは、さまざまなQマップがそれぞれのテスターのパフォーマンスの横に表示されます。これは、リアルタイムのフォワードテストに詳細なロギングを実装し、市場の状態、行動、報酬、Q値の更新をキャプチャすることで、さらに一歩進めることができます。これは、少なくとも紙の上では、意思決定の欠陥を追跡し、必要に応じて学習率やεディケイなどのパラメータを調整するのに役立つはずです。
ストラテジーテスターレポート
EAの使いやすさを実証するために、2022年のユーロ円の日足でテストをおこなったところ、以下のような結果が得られました。

ライブ取引におけるSARSAの実践的考察
SARSAはオンポリシーアルゴリズムであるため、学習中にポリシーの行動を直接取り込み、ノイズの多いデータに対して、より適応的に対応できます。本記事で使用しているQマップの値メソッドは、上記のオンポリシーのコードに示されているように、マップ内のすべてのQ値を、現在の行動からのギャップに比例して更新します。この更新は、探索(新しい戦略の発見)と活用(既知の戦略の利用)のバランスを取るために、行動関数で実行されるε-greedy更新に続くもので、モデルが市場データの短期的なノイズに過剰に適合するのを避けるのを支援します。エージェントが使用する次の行動は、前述のQ学習の記事で説明したように、マルコフ重み付けがQ値更新プロセスに適用されるかどうかに関係なく、マルコフ決定プロセスを通じて選択されます。これは以下に示すように、関数Action内で処理されます。
//+------------------------------------------------------------------+ // Choose an action using epsilon-greedy approach //+------------------------------------------------------------------+ void Cql::Action(vector &E) { int _best_act = 0; if (double((rand() % SHORT_MAX) / SHORT_MAX) < THIS.epsilon) { // Explore: Choose random action _best_act = (rand() % THIS.actions); } else { // Exploit: Choose best action double _best_value = Q[0][e_row[0]][e_col[0]]; for (int i = 1; i < THIS.actions; i++) { if (Q[i][e_row[0]][e_col[0]] > _best_value) { _best_value = Q[i][e_row[0]][e_col[0]]; _best_act = i; } } } //update last action act[1] = act[0]; act[0] = _best_act; // int _e_row_new = 0, _e_col_new = 0; SetMarkov(int(E[E.Size() - 1]), _e_row_new, _e_col_new); e_row[1] = e_row[0]; e_col[1] = e_col[0]; e_row[0] = _e_row_new; e_col[0] = _e_col_new; LetMarkov(e_row[1], e_col[1], E); int _next_state = 0; for (int i = 0; i < int(markov.Cols()); i++) { if(markov[int(E[0])][i] > markov[int(E[0])][_next_state]) { _next_state = i; } } int _next_row = 0, _next_col = 0; SetMarkov(_next_state, _next_row, _next_col); transition_act = 0; for (int i = 0; i < THIS.actions; i++) { if(Q[i][_next_row][_next_col] > Q[transition_act][_next_row][_next_col]) { transition_act = i; } } }
報酬による時間的平滑化メカニズムは、複数の時間反復にわたる累積報酬に注目することで、短期的なノイズを平滑化するのに役立ちます。これにより、アルゴリズムはデータ中の短期的なノイズを超えたパターンを学習できるようになります。また、継続的な方策調整により、方策が現在と将来の両方の状態に基づくことが保証されます。これは、市場の状況が急速に変化している場合に、より多くのデータが利用可能になるにつれてアルゴリズムが適応し、ノイズの多いデータの影響を軽減するのに役立ちます。
SARSAは、上記でコードを示したAction-functionで示されるように、トランジション行動を選択する固有の構造を持つため、ボラタイルな市場に非常に強いです。現在の環境状態を表す座標のペアが与えられた場合、この2つの値をQLクラスのマルコフ行列で認識可能な1つのインデックスに変換する必要があります。これはGetMarkov関数を使うことで実現します。
//+------------------------------------------------------------------+ // Getting markov index from environment row & col //+------------------------------------------------------------------+ int Cql::GetMarkov(int Row, int Col) { return(Row + (THIS.environments * Col)); }
このインデックスで武装すれば、マルコフ行列の行(このインデックスで表される)から、どの列が最も高い確率値を持っているかを確認できます。このマルコフ行列は、一般的なインジケーターのようにバッファリングも保存もされないので、特に適しています。メモリレスであるため、ボラティリティが高いような不確実な環境にも非常に適応しやすいです。つまり、現在の環境状態を表すマルコフ行列の行から、最も確率の高い列を読み出し、そのインデックスから次の環境状態を表す整数を得る。この整数は、最初に使用したのと同様に、行インデックスと列インデックスと呼ばれる2つの値に分解する必要があります。
この行と列の値が次の状態の座標を表すと、Qマップから最も高いQ値を持つ行動を読み出すことができます。この行動のインデックスは、私たちが「トランジション行動」と呼んでいるものを表しています。つまり、「0-売り」、「1-何もしない」、「2-買い」です。また、Qマップが考慮する3つの「時間枠」において、環境は3つの状態に制限されます。
私が「時間枠」と言っているのは、実際には1つの時間枠を使っているからです。もちろん、読者はコードを修正し、それに応じて変更できます。入力パラメータ「m_scale」は、環境状態のマッピングと定義に使用する2層の変更を取得する際に、より大きな「時間枠」の変更がどの程度追跡されるかを定義します。
SARSAは、現在の方策の中で、行動と報酬の両方を考慮し、市場のボラティリティが高い時期に学習プロセスを安定させるのに役立っています。これにより、市場の急激な変化から生じる意思決定の極端な変動を防ぐことができるため、Q学習のような非方策的なアルゴリズムに比べ、ボラティリティの高い市場に適しています。上で共有したオンポリシー関数のコードからわかるように、Q学習アルゴリズムの場合のようにインデックス0ではなく、インデックス1の行動の値を更新します。
SARSAは、現在の方策によって取られた行動を直接評価するため、それほど大騒ぎすることなく、ボラティリティの高い環境ではより慎重になり、その結果、予想外の市場シフトの際に誤った意思決定につながる可能性のある、過度に楽観的な行動価値推定を避けることができます。また、ボラティリティの高い市場では、SARSAのε-greedy探索により、モデルはリスクの高い行動を取る代わりに、より安全な戦略を探索できます。これにより、価格が極端に変動する時期に大きな損失が発生する可能性を減らすと同時に、モデルには新たな収益戦略を発見する機会を与えることができます。より安全な戦略の探索は、εによって行動がランダムに選ばれることにより、必ずしも現在の最良の報酬が得られる行動が選ばれるわけではないことに起因しています。不安定な状況では、すぐに悲惨な報酬に変わってしまう可能性があります。
SARSAが長期的に最適な方策に収束するためには、環境との継続的な相互作用が必要です。長期的なトレンドや構造的な変化が常態化している金融市場では、SARSAの反復的な方策評価により、Q値は時間の経過とともに収束する傾向にあるため、こうした長期的なパターンが反映されることになります。
より複雑な戦略のためのSARSAのカスタマイズ
SARSAにおける状態空間集約は、特に金融市場において、状態空間(価格変動、指標、市場状況、経済カレンダーニュース)が非常に大きく連続的であるような複雑な状態空間を分解する手段となります。状態空間集約は、似たような状態を「集約された」または「抽象的な」状態にグループ化することで、この複雑さを軽減します。この記事での最も単純な例は、即時の変化とより長期間にわたる変化に関する3つの状態に過ぎません。ただし、これをより有効に活用するには、たとえば各軸に10年利回り値情報、ベンチマーク金利、生産者物価指数(PPI)、消費者物価指数(CPI)、失業率などが含まれるQマップなど、より多面的な環境を考慮する必要があります。
Qマップは二軸型であるため、この情報は取引されるFXペアの各通貨に適用されます。したがって、これらの指標の個々の値を扱う代わりに、単純に各指標が増加したのか、横ばいなのか、減少したのかを考慮することで、この記事と同じようにまとめることができます。この結果に基づいて、Qマップ行列の各点にどのインデックスを割り当てるかを決定します。これは、この記事でQマップにインデックスを割り当てた方法と同様です。
結論
この記事では、強化学習のもう1つのアルゴリズムであるSARSAについて考察しました。また、最初のRLアルゴリズム(Q学習を考慮したもの)およびその後のDeep-Q-Networksを扱ったアルゴリズムの実装が、次の行動を選択する際に適切にマルコフ決定過程を使用していなかったことを指摘する価値があります。その代わりに、マルコフ連鎖は単に更新プロセスに重みをつけるメカニズムとして提供されました。お詫びして、この記事のために訂正し、完全なソースコードを添付します。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16143





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索