English
preview
古典的な戦略を再構築する(第15回):デイリーブレイクアウト取引戦略

古典的な戦略を再構築する(第15回):デイリーブレイクアウト取引戦略

MetaTrader 5 |
23 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

この連載において、取引戦略のパフォーマンス向上のために機械学習モデルを用いることがよくあります。しかし、これらのモデルは与えられたデータに対して多くの盲目的な前提を置いていることを見落としがちです。さらに、古典的な統計学習は、自分がモデル化しようとしている関係が実際に現実世界に存在するかどうかを証明する手段を提供してくれません。

一方、人間のトレーダーは市場との長年の対話を通じて、ある種の「市場ロジック」の本質を学んできました。この洞察は経験からのみ得られるものであり、ここではこれを市場ロジックと呼ぶことにします。私たちは、この洞察を数値主導の取引アプリケーションに統合できればと考えています。裁量トレーダーはコンピュータが存在するよりも遥か以前から市場に参加してきました。そのため、彼らが長年培ってきた経験則の中には、我々が利用可能な価値ある真理が含まれているかもしれません。

すでに述べたように、私たちは通常、過去から未来を予測する関係を学習するために機械学習アルゴリズムを使用しています。しかし、そのような関係が存在し、データから学習可能であるという前提を、私たちはほとんど疑うことがありません。にもかかわらず、その関係が実際に存在するかどうかを証明する手間をかけることは稀です。一方で裁量トレーダーは、キャリアを維持するために、信頼できる関係性を探し続けることを強いられてきました。この観点からすれば、人間のトレーダーは、我々の機械学習モデルが基盤として活用できる、手作業による検証を先におこなってきたとも言えます。

したがって私たちは、長年の市場経験から人間が築き上げてきたヒューリスティクスや経験則を活用し、機械学習と金融取引のギャップを埋めたいと考えています。トレーダーが一般的に従ってきたこれらのルールは、モデルが金融市場をより構造的に学習するためのフレームワークとして機能する可能性があります。モデルに関係性の証明を一から求めるのではなく、時間をかけて有効性が示されてきた原理を拡張する形で利用できるため、モデルにとって大きな利点となります。

テストにおいて最も重要なのは、出発点となる戦略の有効性を証明することです。このため、本記事で扱う取引戦略は、連続する取引日の関係性に基づく、よく知られたブレイクアウト手法を採用しました。この戦略の本質は非常にシンプルです。


取引戦略の概要

各取引日の開始時に、まず前日の高値と安値をマーキングします。

図1:前日の高値と安値をマーキングしてデイリーブレイクアウト戦略を準備する

価格が前日の高値をブレイクした場合、ショートポジションをとり、前日の安値で利益確定します。逆に、価格が前日の安値をブレイクした場合はロングポジションをとり、前日の高値で利益確定します。これは本質的に逆張り戦略であり、1日に1回のブレイクアウトを狙うシンプルな手法です。

図2:前日の極値を初めてブレイクしたタイミングでエントリーシグナルが発動する

興味深いことに、予測モデルや高度なニューラルネットワークを用いなくても、金融市場の仕組みに対する健全な理解が非常に有用であることがわかりました。原始的な形ではあるものの、このデイリーブレイクアウト戦略は、裁量トレーダーが経験から学んだ市場ロジックだけで、ディープニューラルネットワークに匹敵する精度を達成しました。

図3:反対側の極値をブレイクしたタイミングでポジションをクローズする際に含まれる一定の誤差

しかし同時に、この戦略が過度に攻撃的かつ不安定であることも確認しました。図3に示した通り、戦略が定義するエグジット条件が常に完全には満たされないため、ボラティリティ(バックテスト中の口座残高の変動)を抑制するための調整が必要でした。

以降のセクションでは、まず多くのトレーダーが受け入れている原型の戦略を、可能な限りシンプルな形で実装し、そのパフォーマンスを基準として確立します。その後、戦略に対して改良を施し、オリジナル版と比較することで、改善が望ましい方向に進んでいることを検証しました。今回の変更は、以下の3つの重要な側面に影響を与えています。

収益性

元の戦略は、5年間のバックテストでわずか40ドルの利益しか生み出しませんでした。これはどの基準で見ても優れた成績ではありません。しかし、驚くべき点は、全取引の68%が利益を上げていたことです。パラメータ調整を一切おこなわず、論理と直感のみで構築された戦略としては、5年間のアウトオブサンプルテストにおいて非常に高い精度と言えます。ただし、平均損失が平均利益を上回る構造が足を引っ張り、結果として40ドルという低利益に留まっていました。

数回の改良の結果、戦略は同期間で162ドルの利益を生み、利益は300%向上しました。さらに、シャープレシオは0.13から0.40へと改善し、207%の向上となりました。ここで強調しておきたいのは、全ての取引を最小ロット固定で行い、アプリケーションが、本当の実力を発揮できるようにした点です。

取引効率

当初の戦略は5年間で886回ものエントリーをおこない、その結果として40ドルしか得られませんでした。一方、改良版では回数は246回にまで削減され、162ドルの利益を生み出しました。これは、取引量と市場エクスポージャーが72%削減されたにもかかわらず、より高い利益を得られたことを意味します。リスクを減らしながら利益を増やすという点で、これは取引システムにおける非常に望ましい特徴です。

また、元の戦略では平均利益が3.64ドル、平均損失が7.87ドルでしたが、改良版では平均利益を14.82ドルへと向上させつつ、平均損失は7.95ドル程度に抑えることができました。 

取引精度

残念ながら、こうした改善は収益性と効率を大きく高めた一方で、取引精度を低下させました。勝率は68%から37%へと減少し、45%の低下となりました。元の戦略の魅力のひとつは高い精度にあったため、これを維持しつつ改善する手動調整は容易ではありませんでした。

そのため、この調整には前回紹介したようにフィードバックコントローラを用いるアプローチも検討できます。また、長年にわたり多くの裁量トレーダーが依拠してきた経験則をさらにテストし、今回のように自然と高い精度を生み出すものを選別することも価値があると考えています。こうした戦略は、人間の直感の強みと弱みの両方を機械学習モデルが学習するための、より構造化されたフレームワークを提供し得るでしょう。それでは始めましょう。


MQL5の始め方

ほとんどのアプリケーションと同様に、まずは戦略に従うために必要なグローバル変数を定義します。追跡する2つの価格レベル(前日の高値と安値)の識別子が必要になります。また、これらの価格レベルをチャート全体に走る水平線でマークするため、価格レベルの名前を格納する文字列が必要になります。

//+------------------------------------------------------------------+
//|                                                         UB 2.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/ja/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/ja/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double last_high,last_low;
string h,l;
bool rest;
int atr_handler;
double atr[];

同様に、バックテスト中にポジションを管理するために、取引ライブラリをインポートします。

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

アプリケーションが初めて読み込まれた際には、まず監視対象となる2つの価格レベルの名称を保存し、リスク管理のためにAverage True Range (ATR)インジケーターをセットアップします。最後に、「rest」という名前のシステムフラグをリセットします。名前が示す通り、このフラグがfalseの間はシステムは取引機会を探索し続けます。restがtrueに設定された時点で、システムは新たなセットアップの検索を停止し、代わりに既存のポジション管理へと移行します。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   h="high";
   l="low";
   rest = false;
   atr_handler = iATR(Symbol(),PERIOD_H8,4*14);
//---
   return(INIT_SUCCEEDED);
  }

アプリケーションが使用されなくなったら、ATRインジケーターを解放してクリーンアップし、システムの残りの部分とメモリを共有します。 

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(atr_handler);
  }

毎日の初めに、前日に提示された最高価格と最低価格のレベルを一度保存します。

//---
   datetime cd = iTime(Symbol(),PERIOD_D1,0);
   static datetime ds;

   if(cd != ds)
     {
      ds = cd;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);

      if((rest==true) && (PositionsTotal() == 0))
         rest = false;

      Comment("Last High: ",last_high,"\nLast Low: ",last_low);

      ObjectDelete(0,l);
      ObjectDelete(0,h);

      ObjectCreate(0,h,OBJ_HLINE,0,0,last_high);
      ObjectCreate(0,l,OBJ_HLINE,0,0,last_low);
      CopyBuffer(atr_handler,0,0,1,atr);
     }

取引日が進行するにつれて、どちらか一方の極値が最初にブレイクされたタイミングで逆張りポジションをとります。この最初のブレイクこそが、その日の真のトレンドであると考えられているためです。

   datetime ch = iTime(Symbol(),PERIOD_H1,0);
   static datetime hs;

   if(ch != hs)
     {
      hs = ch;
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {

            if(close<last_low)
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high);
               
            else
               if(close>last_high)
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low);
                  
            rest = true;
           }
        }
     }
  }
//+------------------------------------------------------------------+

この戦略は主に直感に基づいているため、可動部分は多くありません。アプリケーションに必要な設定が完了したので、ここからバックテストを開始できます。

まず、アプリケーションの動作を確認するために、過去5年分のEUR/USDデータを選択します。  

図4:元のトレーディング戦略を用いてベースラインとなるパフォーマンスを確立する

続いて、実際の取引における予測不能性をシミュレートするため、ランダム遅延を使用するように遅延設定を構成します。

図5:ランダム遅延設定を使用することで、より信頼性の高いシミュレーション環境を構築する

その後、時間経過に応じて口座残高(エクイティ)をプロットすると、戦略は非常に単純で素朴なものにもかかわらず、エクイティにわずかな上昇トレンドを生み出していることが確認できます。しかし、エクイティカーブの山と谷が大きく離れており、戦略の攻撃性を抑え、よりコントロールされた動作を実現するための改善が必要です。

図6:元のトレーディング戦略が生成したエクイティカーブは一貫性に欠け、ボラティリティが高すぎる

それでも、元の戦略が5年間のバックテストで実現した総利益はわずか40ドルに過ぎませんでした。とはいえ、非常に印象的だったのはその高い精度です。戦略が実行した全取引のうち、68%が利益となっていました。しかし問題は、平均利益が平均損失よりも小さかったことです。損失は利益のほぼ2倍に達していました。

図7:元の戦略の詳細統計。自然発生的に高い勝率は非常に注目に値する


改善の実施

そのため、元の戦略を改善するにあたり、戦略が市場をどのように分析しているのかを改めて考え、1日を通して価格レベルのブレイクを待たずに動きを先読みできる方法がないかを検討する必要がありました。十分に検討した結果、各日の最初の1時間に、その日の始値と前日の極値(高値と安値)の位置関係を比較するというアプローチにたどり着きました。

これにより、前日の極値と当日の始値とのギャップを把握できます。直感的には、価格はより大きなギャップ方向へ動きやすいと考えられます。よって、始値が前日の安値により近い場合はロングポジションをとり、逆に、始値が前日の高値により近い場合は、ショートポジションをとります。

図8:元の取引戦略に対して実施する改善の可視化

この改善を実装したところ、元の40ドルから総純利益が増加しました。これにより、従来のルールに従って丸一日待つことなく、その日の方向性を先読みしてエントリーできるようになりました。エントリールールに必要な変更はわずかで、現在のクローズと前日の高値・安値との絶対距離を比較するだけです。高値との差が大きければ、ロングポジションをとり、それ以外の場合は、ショートポジションをとります。エントリー後はrestパラメータをセットし、ポジションが解消されるまで新たなエントリーポイントは探しません。

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {
            //High Gap Is Bigger
            if(MathAbs(close - last_high) > MathAbs(close - last_low))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high);
	       rest = true;

            
            //Low Gap Is Bigger
            else
               if(MathAbs(close - last_high) < MathAbs(close - last_low))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low);
	          rest = true;
           }
        }

これらを組み合わせると、新しいアプリケーションは次のようになります。

//+------------------------------------------------------------------+
//|                                                         UB 2.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/ja/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/ja/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double last_high,last_low;
string h,l;
bool rest;
int atr_handler;
double atr[];

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   h="high";
   l="low";
   rest = false;
   atr_handler = iATR(Symbol(),PERIOD_H8,4*14);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(atr_handler);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   datetime cd = iTime(Symbol(),PERIOD_D1,0);
   static datetime ds;

   if(cd != ds)
     {
      ds = cd;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);

      if((rest==true) && (PositionsTotal() == 0))
         rest = false;


      Comment("Last High: ",last_high,"\nLast Low: ",last_low);

      ObjectDelete(0,l);
      ObjectDelete(0,h);

      ObjectCreate(0,h,OBJ_HLINE,0,0,last_high);
      ObjectCreate(0,l,OBJ_HLINE,0,0,last_low);
      CopyBuffer(atr_handler,0,0,1,atr);
     }

   datetime ch = iTime(Symbol(),PERIOD_H1,0);
   static datetime hs;

   if(ch != hs)
     {
      hs = ch;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {
            if(MathAbs(close - last_high) > MathAbs(close - last_low))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high);

            else
               if(MathAbs(close - last_high) < MathAbs(close - last_low))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low);

            rest = true;
           }
        }
     }
  }
//+------------------------------------------------------------------+

改善後の戦略が生成したエクイティカーブは、元の戦略では到達できなかった新たな高値を示しました。総純利益は40ドルから148ドルへ増加しています。しかし同時に、勝率が低下し始めている兆候も確認できました。

図9:改善後の戦略が生成したエクイティカーブの可視化

一方で、効率は確実に向上しました。元の戦略では886回のエントリーで+40ドルだったのに対して、改善後の戦略では469回のエントリーで+148ドルとなりました。エントリー回数を約半分に削減したにもかかわらず、利益は108ドル増加しています。戦略は明らかに正しい方向へ進んでいますが、さらなる改善の余地が残されています。

図10:元の戦略に対しておこなわれた改善の統計分析


さらなる改善の実施

これを検討するため、以下の図を見てみましょう。図では、前日の高値ギャップを緑色の矩形、前日の安値ギャップを青色の矩形で表しています。これまでの作業仮説では、価格はギャップの大きい方向へ動きやすいと考えてきました。現在の戦略では、価格が前日の極値をブレイクしたタイミングで利益確定しています。 

図11:緑と青の矩形は、それぞれ前日の高値・安値ギャップを表す

しかし、図のように、価格は常に極値を完全にブレイクするわけではありません。極値に近づくだけで到達せず、反転してしまう場合があります。

図12:元の戦略のエグジット条件は、市場状況によっては一貫して満たされない

このため、テイクプロフィット(TP)を前日の極値よりさらに上に置くのではなく、極値のすぐ手前、たとえばATRの一部だけ手前に設定するという方法が考えられます。これなら、価格が極値に近づいた段階で確実に利益を確保できます。

加えて、ポジションが含み益方向に進むにつれて、ストップロス(SL)をよりタイトに引き上げることで、可能な限り多くの利益を確保したいと考えました。そのためにルールを変更し、従来のようにSLとTPに同じパディングを加えるのではなく、TPには小さめのパディング、SLにはやや大きめのパディングを加えるようにしました。これにより、SLは適度に余裕を持たせつつ、TPはタイトに保つことでモメンタムを捉えやすくします。

さらに、ポジションがオープンしている間は、新しいSL提案が現在のものより有利かどうかを条件付きで確認します。可能であれば、SLは常に最新の極値に紐づく形が理想です。

   if(ch != hs)
     {
      hs = ch;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {

            if((MathAbs(close - last_high) > MathAbs(close - last_low)))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high+(padding*0.9));

            else
               if((MathAbs(close - last_high) < MathAbs(close - last_low)))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low-(padding*0.9));

            rest = true;
           }
        }

      if(rest == true)
        {
         if(PositionsTotal() > 0)
           {
            if(PositionSelectByTicket(PositionGetTicket(0)))
              {
               double sl,tp;

               sl = PositionGetDouble(POSITION_SL);
               tp = PositionGetDouble(POSITION_TP);

               //--- Buy
               if(sl < tp)
                 {
                  if(last_low > sl)
                     Trade.PositionModify(Symbol(),last_low,tp);
                 }

               if(tp < sl)
                 {
                  if(last_high < sl)
                     Trade.PositionModify(Symbol(),last_high,tp);
                 }
              }
           }
        }
     }

これらをまとめると、現時点でのアプリケーションの最終バージョンが完成しました。

//+------------------------------------------------------------------+
//|                                                         UB 2.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/ja/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/ja/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double last_high,last_low;
string h,l;
bool rest;
int atr_handler;
double atr[];

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   h="high";
   l="low";
   rest = false;
   atr_handler = iATR(Symbol(),PERIOD_H8,4*14);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
      IndicatorRelease(atr_handler);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   datetime cd = iTime(Symbol(),PERIOD_D1,0);
   static datetime ds;

   if(cd != ds)
     {
      ds = cd;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);

      if((rest==true) && (PositionsTotal() == 0))
         rest = false;

      Comment("Last High: ",last_high,"\nLast Low: ",last_low);

      ObjectDelete(0,l);
      ObjectDelete(0,h);

      ObjectCreate(0,h,OBJ_HLINE,0,0,last_high);
      ObjectCreate(0,l,OBJ_HLINE,0,0,last_low);
      CopyBuffer(atr_handler,0,0,1,atr);
     }

   datetime ch = iTime(Symbol(),PERIOD_H1,0);
   static datetime hs;

   if(ch != hs)
     {
      hs = ch;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {

            if((MathAbs(close - last_high) > MathAbs(close - last_low)))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high+(padding*0.9));

            else
               if((MathAbs(close - last_high) < MathAbs(close - last_low)))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low-(padding*0.9));

            rest = true;
           }
        }

      if(rest == true)
        {
         if(PositionsTotal() > 0)
           {
            if(PositionSelectByTicket(PositionGetTicket(0)))
              {
               double sl,tp;

               sl = PositionGetDouble(POSITION_SL);
               tp = PositionGetDouble(POSITION_TP);

               //--- Buy
               if(sl < tp)
                 {
                  if(last_low > sl)
                     Trade.PositionModify(Symbol(),last_low,tp);
                 }

               if(tp < sl)
                 {
                  if(last_high < sl)
                     Trade.PositionModify(Symbol(),last_high,tp);
                 }
              }
           }
        }
     }
  }

改善後の戦略が生成したエクイティカーブは、これまでのどのバージョンよりも高い水準に到達しました。勝ち負けの上下動(ボラティリティ)も以前より抑えられています。

図13:最終版の取引戦略が生成した、望ましい構造のエクイティカーブ

詳細な統計を見ると、総純利益は前バージョンの148ドルから162ドルへとさらに増加しています。しかし同時に、勝率は以前よりも低下しており、元の戦略よりも低くなっています。これは、改善が勝ち取引だけでなく、望ましくない形で負け取引にも影響している可能性を示唆しています。

これにより、重要な課題が浮かび上がります。人間の直感に基づいて構築された戦略を、さらに直感で改善することには限界があるということです。今後さらに前進するためには、人間の判断を超えたアプローチを検討する必要があります。

図14:詳細な分析により、改善がシステムの精度に負の影響を与えている点が明らかになった


結論

人間のトレーダーが金融市場で生き残ってきたのは、偶然でも、一度の成功を積み重ねてきただけでもありません。彼らは、必要性と損失への恐怖に駆り立てられ、実在し、信頼できる関係性を探し続けてきました。今日ではこの規律が見落とされがちであり、多くの機械学習の実践者は、大量のデータや複雑なモデルへと急ぐあまり、その指標が本当に意味のある進歩を示しているのかを問い直すことがありません。

本記事で示した内容は、別の道筋があることを示唆しています。裁量的な市場ロジックに戦略の基盤を置くことで、私たちは盲目的な仮定ではなく、実証済みの有効性から出発できます。そして、このような戦略に残される誤差、すなわち市場ロジックが捉えきれなかった部分こそが、機械学習が学ぶべき豊かな素材となります。これによりアルゴリズムは、関係性をゼロから証明する必要がなくなり、すでに構造化された入力と出力の枠組みの中で学習できるようになります。

この転換は、私たちのモデルが暗黙のうちに依拠している統計的前提条件を満たす方向へと近づける効果もあります。固定された戦略を変化する市場に適用すると、その戦略が生み出す結果は、堅牢な学習に必要とされる独立同分布に近い性質を帯びはじめます。現実の市場が完全に独立同分布となることは決してありませんが、このアプローチによってその理想に一歩近づくことができ、モデルはより脆弱性が減り、信頼性が高まります。私たちが信じているのは、人間が培ってきたマーケットロジックを出発点とし、そのロジックの誤差を対象としてモデルを学習させることで、機械学習モデルをより高い基盤から学習させられるようになるということです。


ファイル名 ファイルの詳細
Daily Breakout Strategy V1.mq5 積極的な取引戦略で、最初の取引精度は高かったものの、収益性は限定的な初期バージョン 
Daily Breakout Strategy V2.mq5 取引精度はやや低下したものの、収益性が大幅に向上した第2バージョン
Daily Breakout Strategy V3.mq5 最も高い利益を生み出した一方、取引精度は最も低くなっている最終バージョン

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19130

知っておくべきMQL5ウィザードのテクニック(第79回):教師あり学習でのゲーターオシレーターとA/Dオシレーターの使用 知っておくべきMQL5ウィザードのテクニック(第79回):教師あり学習でのゲーターオシレーターとA/Dオシレーターの使用
前回の記事では、ゲーターオシレーターとA/Dオシレーターの組み合わせについて、通常の設定における生のシグナルを用いた場合の挙動を確認しました。この2つのインジケーターは、それぞれトレンド指標と出来高指標として相補的に機能します。今回の記事では、その続編として、教師あり学習を活用することで、前回レビューした特徴量パターンの一部をどのように強化できるかを検証します。この教師あり学習アプローチでは、CNN(畳み込みニューラルネットワーク)を用い、カーネル回帰およびドット積類似度を活用して、カーネルやチャネルのサイズを決定しています。今回もこれまでと同様に、MQL5ウィザードでエキスパートアドバイザー(EA)を組み立てられるようにしたカスタムシグナルクラスファイル内で実装しています。
プライスアクション分析ツールキットの開発(第37回):Sentiment Tilt Meter プライスアクション分析ツールキットの開発(第37回):Sentiment Tilt Meter
市場センチメントは、価格変動に影響を与える要因の中でも最も見落とされがちでありながら強力な要因のひとつです。多くのトレーダーが遅行指標や経験則に頼る中、Sentiment Tilt Meter (STM) EAは生の市場データを明確で視覚的なガイダンスへと変換し、市場が強気、弱気、中立のどちらへ傾いているのかをリアルタイムで示します。これにより、エントリーの根拠を確認し、ダマシを回避し、市場参加のタイミングをより適切に図りやすくなります。
初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(IX) - ニュース取引のための単一チャートでのマルチペア管理 初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(IX) - ニュース取引のための単一チャートでのマルチペア管理
ニュース取引では、ボラティリティが高まるため、非常に短時間で複数のポジションや通貨ペアを管理する必要があります。本記事では、News Headline EAにこの機能を統合することで、マルチペア取引の課題にどのように対応できるかを解説します。MQL5を用いたアルゴリズム取引により、マルチペア取引を効率的かつ強力に実現する方法を一緒に探っていきます。
MQLを使用したFirebaseでのCRUD操作 MQLを使用したFirebaseでのCRUD操作
この記事では、FirebaseのCRUD(作成、読み取り、更新、削除)操作を習得するためのステップバイステップガイドを提供します。Realtime DatabaseおよびFirestoreを中心に、Firebase SDKのメソッドを活用して、Webやモバイルアプリで効率的にデータを管理する方法を解説します。新しいレコードの追加から、データの検索、修正、削除まで、実践的なコード例とベストプラクティスを紹介し、リアルタイムでのデータ構造と操作方法を理解することで、開発者がFirebaseの柔軟なNoSQLアーキテクチャを活かして、動的でスケーラブルなアプリケーションを構築できるようになります。