English Русский 中文 Español Deutsch Português
preview
決闘者アルゴリズム

決闘者アルゴリズム

MetaTrader 5 — トレーディング |
20 2
Andrey Dik
Andrey Dik

目次

  1. はじめに
  2. アルゴリズムの実装
  3. テスト結果


はじめに

アルゴリズム取引では、わずか1ミリ秒の差が利益にも損失にもつながります。そのため、取引戦略の最適なパラメータを見つけることは、極めて重要な課題です。 現代のトレーダーは、取引システムのパラメータを最適化するために、人工知能や進化アルゴリズムを活用しています。

今回は、古くからおこなわれてきた決闘に着想を得た、新しい最適化手法である「決闘者アルゴリズム」について解説します。 このアルゴリズムは、Biyanto氏率いるインドネシアの科学者グループによって2015年に開発されました。同氏らは、勝者と敗者に異なるアプローチを適用することで、突然変異や交叉の各演算子が持つ「盲目的」な性質を最小限に抑え、従来の進化アルゴリズムに代わる手法を目指しました。 この記事では、決闘者アルゴリズムの数学的な基礎を詳しく解説し、MQL5で実装するとともに、他の集団ベースの最適化手法との比較分析をおこないます。


アルゴリズムの実装

取引の現場を、さまざまな戦略が絶えず競い合う闘技場だと考えてみましょう。 ある市場環境では成功する戦略もあれば、うまく機能しない戦略もあります。 では、優れたトレーダーを生み出すものは何でしょうか。 それは、勝利からも敗北からも学び、絶えず状況に適応しながら、自らのアプローチを磨き続ける能力です。

決闘者アルゴリズムは、まさにこの考え方を体現しています。 すべての個体を等しく扱う従来の遺伝的アルゴリズムとは異なり、このアルゴリズムでは勝者と敗者の違いを明確に捉えます。 敗者は学習します。勝者の戦略を分析し、その成功要素を取り入れます。一方、勝者は新たな可能性を探ります。中核となる戦略の有効性がすでに示されているため、そこからさらに新しいアプローチを試みます。 そして、チャンピオンは指導役となり、優れた戦略を次世代へと伝えます。

イラスト

図1: DA_duelistアルゴリズムの動作

アルゴリズムの主要な段階を示したこの図では、決闘者アルゴリズムの重要な仕組みを確認できます。初期集団では、すべての決闘者に同じチャンスが与えられます。チャンピオンの選定では、特に優れた決闘者が金色で強調表示されます。 決闘の段階では、対戦を通じて勝者と敗者が決まります。 学習と革新の段階では、勝者と敗者がそれぞれ異なる経路で進化します。 新たな決闘者の育成では、チャンピオンが自身の能力を次世代に伝えます。そして淘汰の段階では、成績の最も悪い決闘者が集団から除外されます。

図にはさらに、アルゴリズムが反復的に実行されることを示すループ矢印、最終的に選ばれたチャンピオン、および各要素と主要なパラメータを説明する詳細な凡例も含まれています。 この図解によって、読者はアルゴリズムの基本的な仕組みを直感的に理解できます。

それでは、仕組みを詳しく確認したところで、アルゴリズムの擬似コードを記述してみましょう。

入力:
- popSize:決闘者の集団サイズ
- luckCoefficient:決闘における運の係数
- learningProbability:敗者が学習する確率
- innovationProbability:勝者が革新をおこなう確率
- championsCount:チャンピオンの数

初期化:
1. championsCountを確認し、必要に応じて調整する。:
- 1未満の場合は、1に設定する。
- popSize以上の場合は、popSize / 4に設定する。
2. 空のwinners配列とlosers配列を作成する。
3. championsCount個の要素を持つchampions配列を作成する。

メインループ(Moving):

最初の反復の場合:
0からpopSizeまでの各決闘者 i について:
各座標jについて:
許容範囲内のランダムな値で初期化する。
revisionフラグをtrueに設定する。
関数を終了する。

それ以外(以降の反復)の場合:
1. 集団の拡張
- 決闘者の配列のサイズをpopSize + championsCountに拡張する。
- 新たに追加した決闘者の構造体を初期化する。

2. チャンピオンの決定
- 適応度に基づいて決闘者をソートする(バブルソート)。
- 上位championsCount人の決闘者をチャンピオンに選定する。

3. 新しい決闘者の育成
各チャンピオンiについて:
TrainNewDuelist(i, popSize + i)を呼び出す。:
各座標cについて:
new_duelist [c] = champion [c] + GaussDistribution ()
許容範囲を超えないように値を調整する。

4. 決闘の実施:
winners配列とlosers配列をクリアする。
championsCountからtotalDuelistsまでの各決闘者iについて:
対戦相手'opponent'をランダムに選択する。
opponent != iの場合:
DetermineWinnerAndLoser(i, opponent):
A_Luck = fitness [A] * (luckCoef + random () * luckCoef)
B_Luck = fitness [B] * (luckCoef + random () * luckCoef)
IF (fitness [A] + A_Luck) >= (fitness [B] + B_Luck):
Aを勝者、Bを敗者とする。
それ以外の場合:
Bを勝者、Aを敗者とする。
勝者と敗者をそれぞれwinners配列とlosers配列に追加する。

5. 改良
a) 敗者の学習
各(loser, winner)のペアについて:
LearningProcess(loser, winner)を呼び出す。
各座標cについて:
random() < learningProbabilityの場合:
loser [c] = winner [c]

b) 勝者の革新:
各勝者について:
InnovationProcess (winner)を呼び出す。
各座標cについて:
random() < innovationProbabilityの場合
winner [c] = random_value_in_range

6. 淘汰:
- すべての決闘者を適応度の降順でソートする。
- 上位popSize人の決闘者だけを残す。

更新(Revision):
各決闘者iについて:
fitness[i] > global_bestの場合:
global_best = fitness [i] として更新する
global_bestをfitness[i]に更新する。最良解の座標を保存する。

停止条件が満たされるまでメインループを繰り返す。

このアルゴリズムの重要な特徴を強調しておきましょう。チャンピオンは決闘に参加せず、新しい決闘者の育成を担います。また、運の係数によってランダム性が導入されるため、決闘の結果は必ずしも適応度だけでは決まりません。 勝者と敗者には、それぞれ異なるアプローチが適用されます。敗者は勝者から学び、勝者は新しい解決策を探ります。 さらに、成績の悪い決闘者を淘汰することで、集団のサイズを一定に保ちます。 それでは、DA_duelistアルゴリズムの実装コードを見ていきましょう。

まず、C_AOクラスを継承するC_AO_DA_duelistクラスを作成します。このクラスでは、パラメータ配列paramsを初期化し、各パラメータに名前とデフォルト値を設定します。

  • SetParams ():params配列に格納された値に基づいて、popSize、luckCoefficientなどの内部変数を更新します。
  • Init ():アルゴリズムを初期化します。 パラメータの探索範囲と刻み幅を定義する配列、およびエポック数を受け取ります。
  • Moving ():決闘者を進化させながら、最適なパラメータを探索する主要な処理を実行します。
  • Revision ():結果を分析し、それに応じて処理を調整します。

変数

  • luckCoefficient:運の係数
  • learningProbability:学習確率
  • innovationProbability:革新確率
  • championsCount:チャンピオンの数
  • winners []:勝者となった決闘者のインデックスを格納する配列
  • losers []:敗者となった決闘者のインデックスを格納する配列
  • champions []:チャンピオンのインデックスを格納する配列
privateメソッド
  • DetermineWinnerAndLoser():決闘の勝者と敗者を決定します。
  • LearningProcess ():勝者の情報を利用して敗者が学習する処理を実装します。
  • InnovationProcess ():勝者が新たな解決策を探索するための革新処理を実装します。
  • TrainNewDuelist ():チャンピオンをもとに新しい決闘者を育成します。
//————————————————————————————————————————————————————————————————————
class C_AO_DA_duelist : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_DA_duelist () { }
  C_AO_DA_duelist ()
  {
    ao_name = "DA";
    ao_desc = "Duelist Algorithm";
    ao_link = "https://www.mql5.com/ja/articles/19093";

    popSize               = 50;    // number of duelists
    luckCoefficient       = 0.01;  // luck coefficient
    learningProbability   = 0.2;   // learning probability for losers
    innovationProbability = 0.1;   // probability of innovation for winners
    championsCount        = 5;     // number of champions

    ArrayResize (params, 5);

    params [0].name = "popSize";               params [0].val = popSize;
    params [1].name = "luckCoefficient";       params [1].val = luckCoefficient;
    params [2].name = "learningProbability";   params [2].val = learningProbability;
    params [3].name = "innovationProbability"; params [3].val = innovationProbability;
    params [4].name = "championsCount";        params [4].val = championsCount;
  }

  void SetParams ()
  {
    popSize               = (int)params [0].val;
    luckCoefficient       = params      [1].val;
    learningProbability   = params      [2].val;
    innovationProbability = params      [3].val;
    championsCount        = (int)params [4].val;
  }

  bool Init (const double &rangeMinP  [],
             const double &rangeMaxP  [],
             const double &rangeStepP [],
             const int     epochsP = 0);

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double luckCoefficient;       // luck coefficient
  double learningProbability;   // learning probability
  double innovationProbability; // innovation probability
  int    championsCount;        // number of champions

  private: //---------------------------------------------------------
  int    winners [];            // winners' indices
  int    losers  [];            // losers' indices
  int    champions [];          // champions' indices

  void   DetermineWinnerAndLoser (int duelistA, int duelistB);
  void   LearningProcess         (int loserIndex, int winnerIndex);
  void   InnovationProcess       (int winnerIndex);
  void   TrainNewDuelist         (int championIndex, int newDuelistIndex);
};
//————————————————————————————————————————————————————————————————————

C_AO_DA_duelistクラスのInit は、アルゴリズムの実行準備をおこなうためのメソッドです。 主な役割は、処理を開始する前に、初期パラメータとデータ構造を初期化することです。

このメソッドは、まず標準の初期化処理を呼び出し、パラメータの範囲とステップ幅を設定します。 標準の初期化処理が失敗した場合は、処理を続行できないことを示すため、falseを返します。 次に、チャンピオン数を調整します。チャンピオン数が1未満の場合は1に設定し、集団サイズ以上の場合は、集団全体の4分の1に減らします。 その後、勝者、敗者、チャンピオンのインデックスを格納する各配列をクリアし、次のアルゴリズム段階に備えて初期化します。 最後に、初期化が正常に完了したことを示すため、trueを返します。

//————————————————————————————————————————————————————————————————————
//--- Initialization
bool C_AO_DA_duelist::Init (const double &rangeMinP  [],
                            const double &rangeMaxP  [],
                            const double &rangeStepP [],
                            const int     epochsP = 0)
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //------------------------------------------------------------------
  if (championsCount < 1) championsCount = 1;
  if (championsCount >= popSize) championsCount = popSize / 4;

  ArrayResize (winners, 0);
  ArrayResize (losers,  0);
  ArrayResize (champions, championsCount);

  return true;
}
//————————————————————————————————————————————————————————————————————

C_AO_DA_duelist クラスのDetermineWinnerAndLoserメソッドは、集団に属する2つの個体(決闘者)間の決闘における勝者と敗者を決定します。 処理の流れを順に見ていきましょう。

  1. 「運」の計算: 各決闘者(AとB)について、運の値(A_LuckおよびB_Luck)を計算します。 運は、各決闘者の基本適応度を表すf値、係数luckCoefficient、およびu.RNDprobab()によって生成されるランダムな値に基づいて決まります。 これにより、運が勝敗に与える影響をシミュレートします。
  2. 勝者の決定: 各決闘者について、適応度fと運の値を合計し、その合計値を比較します。 合計値が相手以上であれば、その決闘者を勝者とします。
  3. 結果の保存: 勝者と敗者のインデックスを、それぞれwinners配列とlosers配列に追加します。 これらの配列は、その後の分析や個体群の進化に使用されます。

    //————————————————————————————————————————————————————————————————————
    //--- Determine the winner and loser in a duel
    void C_AO_DA_duelist::DetermineWinnerAndLoser (int duelistA, int duelistB)
    {
      // Algorithm from the document
      double A_Luck = a [duelistA].f * (luckCoefficient + u.RNDprobab () * luckCoefficient);
      double B_Luck = a [duelistB].f * (luckCoefficient + u.RNDprobab () * luckCoefficient);
    
      if ((a [duelistA].f + A_Luck) >= (a [duelistB].f + B_Luck))
      {
        ArrayResize (winners, ArraySize (winners) + 1);
        ArrayResize (losers,  ArraySize (losers) + 1);
        winners [ArraySize (winners) - 1] = duelistA;
        losers  [ArraySize (losers) - 1] = duelistB;
      }
      else
      {
        ArrayResize (winners, ArraySize (winners) + 1);
        ArrayResize (losers,  ArraySize (losers) + 1);
        winners [ArraySize (winners) - 1] = duelistB;
        losers  [ArraySize (losers) - 1] = duelistA;
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    C_AO_DA_duelistクラスのLearningProcessメソッドは、決闘に敗れた個体が勝者から学習するプロセスを実装しています。 このプロセスの目的は、勝者の戦略の一部を敗者に取り入れることで、敗者のパフォーマンスを向上させることです。 メソッドの処理内容を見ていきましょう。

    座標の反復処理:forループによって、個体が持つすべての座標を順番に処理します。 座標の数は変数coordsによって決まります。

    確率に基づくコピー:ループ内では、u.RNDprobab()を使用してランダムな判定をおこないます。 生成された乱数がlearningProbabilityより小さい場合、次の処理が実行されます。

    特性のコピー:敗者(loserIndex)は、勝者(winnerIndex)が持つc番目の座標の値を取り込みます。 つまり、敗者の対応する特性を、勝者の同じ特性の値で更新します。 これにより、敗者は勝者の戦略の一部をコピーします。

    このように、このメソッドは、より成功した個体から成功していない個体へ知識や戦略が伝達される過程をシミュレートします。これが、進化プロセスの基礎となります。

    //————————————————————————————————————————————————————————————————————
    //--- A loser learns from a winner
    void C_AO_DA_duelist::LearningProcess (int loserIndex, int winnerIndex)
    {
      for (int c = 0; c < coords; c++)
      {
        if (u.RNDprobab () < learningProbability)
        {
          // A loser copies part of a winner's strategy
          a [loserIndex].c [c] = a [winnerIndex].c [c];
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    C_AO_DA_duelistクラスのInnovationProcessメソッドは、決闘に勝利した個体に対して革新、すなわち突然変異を発生させる処理を実装しています。 このプロセスでは、勝者の戦略にランダムな変化を加え、新たな可能性を探ります。

    座標の反復処理:forループによって、LearningProcessと同様に、個体が持つすべてのc座標を順番に処理します。

    確率に基づく突然変異:u.RNDprobab()によって生成された乱数がinnovationProbabilityより小さい場合、突然変異が発生します。 つまり、innovationProbabilityで指定された確率に従って、個体の戦略を変更するかどうかが決まります。

    新しい値の生成と補正:乱数生成関数u.RNDfromCI()は、c番目のパラメータについて、rangeMin[c]からrangeMax[c]までの範囲内でランダムな値を生成します。 次に、u.SeInDiSp()を使用して、その値を補正します。 この関数は、指定された範囲(rangeMin[c]~rangeMax[c])内で、刻み幅rangeStep[c]に従った離散的な値に変換する役割を担います。

    このように、このメソッドでは、勝者が新たな戦略を試す機会を設けています。その結果、個体の特性や環境への適応能力が向上する可能性があります。 これは、進化プロセスを構成する重要な要素です。

    //————————————————————————————————————————————————————————————————————
    //--- The innovation process for a winner
    void C_AO_DA_duelist::InnovationProcess (int winnerIndex)
    {
      for (int c = 0; c < coords; c++)
      {
        if (u.RNDprobab () < innovationProbability)
        {
          // The winner tries a new technique (mutation)
          a [winnerIndex].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
          a [winnerIndex].c [c] = u.SeInDiSp (a [winnerIndex].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    C_AO_DA_duelistクラスのTrainNewDuelistメソッドは、新しい決闘者(個体)を生成し、現在のチャンピオンをもとに学習させることで、その初期設定をおこないます。 これは遺伝の仕組みに似ていますが、ランダム性も取り入れることで、個体群に遺伝的多様性をもたらすプロセスです。

    座標の反復処理: forループによって、新しい決闘者(newDuelistIndex)が持つすべてのcパラメータ(座標)を順番に処理し、それぞれの値を設定します。

    偏差の計算: deviationを計算します。これは、許容される値の範囲に対して、どの程度の変動を認めるかを決定するものです。

    突然変異を伴う遺伝(正規分布): チャンピオン(championIndex)が持つc番目の特性の値に、u.GaussDistribution()関数で生成したランダムな偏差を加えます。 この関数は正規分布(ガウス分布)に従う乱数を生成し、継承する特性にランダムな変動を与えます。 引数の0、rangeMin[c]、rangeMax[c]、8は、正規分布の平均値0、最小値と最大値、および分布の幅を制御するパラメータ8を指定しています。

    離散値への調整(SeInDiSp): 突然変異後の新しい決闘者の特性値を、u.SeInDiSp()関数を使用して離散的な値に変換します。 この関数により、新しい決闘者のパラメータが許容範囲内に収まり、刻み幅rangeStep[c]に従った値になるように調整されます。

    このように、新しい決闘者はチャンピオンに似た特性を受け継ぎながら、ランダムな変動も加わった状態で生成されます。これによって個体群の多様性が生まれ、新たな戦略を探索できるようになります。

    //————————————————————————————————————————————————————————————————————
    //--- A champion trains a new duelist
    void C_AO_DA_duelist::TrainNewDuelist (int championIndex, int newDuelistIndex)
    {
      for (int c = 0; c < coords; c++)
      {
        // The new duelist inherits the champion's abilities with slight variations
        double deviation = (rangeMax [c] - rangeMin [c]) * 0.1;
        a [newDuelistIndex].c [c] = a [championIndex].c [c] + u.GaussDistribution (0, rangeMin [c], rangeMax [c], 8);
        a [newDuelistIndex].c [c] = u.SeInDiSp (a [newDuelistIndex].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    C_AO_DA_duelistクラスのMovingメソッドは、決闘者進化アルゴリズムにおける主要な処理を実行します。 このメソッドには、初期化(初回実行時)、繁殖、選択、決闘、学習(決闘の結果を利用)、革新、および淘汰が含まれます。 進化プロセスの中核となる処理を、順を追って見ていきましょう。

    集団の初期化(初回実行): 初回実行時(revisionがfalseの場合)、集団を初期化します。各個体iについて、すべての特性jの値を、許容範囲と刻み幅に従ってランダムに設定します。 初期化が完了すると、以降の実行時に再初期化されないよう、revisionをtrueに設定します。

    繁殖の準備: 集団を格納する配列aのサイズを拡張し、チャンピオンから新たに生成される決闘者を収容できるようにします。 新しい決闘者を初期化し、それぞれの初期状態を設定します。

    チャンピオンの選出: 適応度を表すfパラメータに基づいて個体群をソートし、優れた個体を選びます。 その中から、上位championsCount個体をチャンピオンとして選出します。 各チャンピオンはTrainNewDuelistメソッドを使用して、自身の特性を受け継ぐ新しい決闘者を生成します。

    決闘の準備: 新たな決闘の結果を記録できるよう、winners配列とlosers配列をクリアします。

    決闘の実行: チャンピオン以外の各個体は、チャンピオンではない個体の中からランダムに選ばれた対戦相手と決闘します。 各決闘の勝者と敗者は、DetermineWinnerAndLoserメソッドによって決定されます。

    決闘者の学習: 敗者は勝者から学習します。 LearningProcessメソッドによって、敗者は勝者の優れた特性を取り入れ、自身のパラメータを変更します。

    勝者の革新: 優れた個体はInnovationProcessメソッドを使用して、自身の戦略に変化を加え、新たな可能性を探ります。

    再ソート: 決闘、学習、革新による変化を反映するため、個体群全体をfパラメータに基づいて再びソートし、個体の順位を更新します。

    最下位個体の淘汰: 配列aのサイズをpopSizeに戻し、成績の悪い個体を取り除くことで、集団の規模を一定に保ちます。

    この一連の処理が、進化の1ステップに相当します。子孫の生成、優れた個体の選出、学習、戦略の更新をおこないながら、集団の規模を維持します。 Movingメソッドは、繁殖、突然変異、選択、学習という進化アルゴリズムの基本的な操作を実行する役割を担っています。

    //————————————————————————————————————————————————————————————————————
    //--- The main step of the algorithm
    void C_AO_DA_duelist::Moving ()
    {
      // Initial population setup
      if (!revision)
      {
        for (int i = 0; i < popSize; i++)
        {
          for (int j = 0; j < coords; j++)
          {
            a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
            a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
          }
        }
    
        revision = true;
        return;
      }
    
      //------------------------------------------------------------------
      // Temporary array expansion for new duelists
      int totalDuelists = popSize + championsCount;
      ArrayResize (a, totalDuelists);
    
      // Initialize new duelists
      for (int i = popSize; i < totalDuelists; i++)
      {
        a [i].Init (coords);
      }
    
      // Sort to determine champions (using bubble sort)
      for (int i = 0; i < popSize - 1; i++)
      {
        for (int j = 0; j < popSize - i - 1; j++)
        {
          if (a [j].f < a [j + 1].f)
          {
            S_AO_Agent temp = a [j];
            a [j] = a [j + 1];
            a [j + 1] = temp;
          }
        }
      }
    
      // Determine the champions
      for (int i = 0; i < championsCount; i++)
      {
        champions [i] = i;
        // A champion trains a new duelist
        TrainNewDuelist (i, popSize + i);
      }
    
      // Clear the winners' and losers' arrays
      ArrayResize (winners, 0);
      ArrayResize (losers,  0);
    
      // Conduct duels (excluding champions)
      for (int i = championsCount; i < totalDuelists; i++)
      {
        // Each duelist fights one random opponent
        int opponent = u.RNDintInRange (championsCount, totalDuelists - 1);
        if (opponent != i)
        {
          DetermineWinnerAndLoser (i, opponent);
        }
      }
    
      // Improving duelists
      int minCount = MathMin (ArraySize (winners), ArraySize (losers));
      for (int i = 0; i < minCount; i++)
      {
        // Losers learn from winners
        LearningProcess (losers [i], winners [i]);
      }
    
      for (int i = 0; i < ArraySize (winners); i++)
      {
        // Winners innovate
        InnovationProcess (winners [i]);
      }
    
      // Sort all duelists
      for (int i = 0; i < totalDuelists - 1; i++)
      {
        for (int j = 0; j < totalDuelists - i - 1; j++)
        {
          if (a [j].f < a [j + 1].f)
          {
            S_AO_Agent temp = a [j];
            a [j] = a [j + 1];
            a [j + 1] = temp;
          }
        }
      }
    
      // Remove the worst duelists
      ArrayResize (a, popSize);
    }
    //————————————————————————————————————————————————————————————————————
    

    C_AO_DA_duelist クラスのRevision メソッドは、現在の集団の中で見つかった最良解に関する情報を更新するために使用されます。 進化の過程を通じて、大域的最適解を追跡することを目的としています。

    集団の走査: forループによって、集団内の各個体(i = 0からpopSize - 1まで)を順番に処理します。

    適応度の比較: ループ内では、現在の個体の適応度fが、これまでに記録された最良の適応度fBよりも優れているかどうかを判定します。

    最良解の更新: 現在の個体の適応度a[i].fがfBよりも優れている場合、fBを更新します。つまり、fBには、それまでに見つかった最良の適応度が記録されます。 さらに、現在の個体が持つc特性の値をcB配列にコピーします。 これにより、cBには、これまでに見つかった最良の個体の特性値が保存されます。

    このように、Revisionメソッドは現在の集団を走査し、最も高い適応度を持つ個体を探します。その個体がこれまでの最良記録を上回っている場合、最良の個体に関する情報(適応度と特性値)を更新します。 このメソッドは、アルゴリズムの進捗を追跡し、大域的最適解を見つけるために重要な役割を果たします。

    //————————————————————————————————————————————————————————————————————
    //--- Update the best and worst solutions
    void C_AO_DA_duelist::Revision ()
    {
      // Update the global best solution
      for (int i = 0; i < popSize; i++)
      {
        if (a [i].f > fB)
        {
          fB = a [i].f;
          ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    


    テスト結果

    今回の検証の結果、総じて、決闘者アルゴリズムは良好な性能を発揮することが確認できました。

    DA|Duelist Algorithm|100.0|0.01|0.9|0.1|2.0|
    =============================
    5 Hilly's; Func runs: 10000; result: 0.9278151663330798
    25 Hilly's; Func runs: 10000; result: 0.5377820196319314
    500 Hilly's; Func runs: 10000; result: 0.27792394907287765
    =============================
    5 Forest's; Func runs: 10000; result: 0.8695700230324329
    25 Forest's; Func runs: 10000; result: 0.47535947112902815
    500 Forest's; Func runs: 10000; result: 0.18193288697223736
    =============================
    5 Megacity's; Func runs: 10000; result: 0.6215384615384616
    25 Megacity's; Func runs: 10000; result: 0.3356923076923076
    500 Megacity's; Func runs: 10000; result: 0.11715384615384725
    =============================
    All score: 4.34477 (48.28%)

    アルゴリズムの動作を可視化した結果、次元数が小さい場合も大きい場合も、値のばらつきが小さいことが確認できます。これは、アルゴリズムが優れた探索性能を備えていることを示しています。

    Hilly

    Hillyテスト関数におけるDA_duelistの動作

    Forest

    Forestテスト関数におけるDA_duelistの動作

    Megacity

    Megacityテスト関数におけるDA_duelistの動作

    テスト結果によると、Duelistアルゴリズムは、検証対象となった最適化アルゴリズム全体の中で42位にランクされています。

    #AO説明HillyHilly
    最終結果
    ForestForest
    最終結果
    Megacity(離散型)Megacity
    最終結果
    最終
    結果
    最大値に対する割合
    (%)
    10 p(5 F)50 p(25 F)1000 p(500 F)10 p(5 F)50 p(25 F)1000 p(500 F)10 p(5 F)50 p(25 F)1000 p(500 F)
    1ANS近傍横断探索0.949480.847760.438572.235811.000000.923340.399882.323230.709230.634770.230911.574916.13468.15
    2CLAコードロックアルゴリズム(joo)0.953450.871070.375902.200420.989420.917090.316422.222940.796920.693850.193031.683806.10767.86
    3AMOm動物移動最適化 M0.903580.843170.462842.209590.990010.924360.465982.380340.567690.591320.237731.396755.98766.52
    4(P+O)ES(P+O)進化戦略0.922560.881010.400212.203790.977500.874900.319452.171850.673850.629850.186341.490035.86665.17
    5CTA彗尾アルゴリズム(joo)0.953460.863190.277702.094350.997940.857400.339492,194840.887690.564310.105121.557125.84664.96
    6TETA時間進化移動アルゴリズム(joo)0.913620.823490.319902.057010.970960.895320.293242.159520.734620.685690.160211.580525.79764.41
    7SDSm確率的拡散探索 M0.930660.854450.394762.179880.999830.892440.196192.088460.723330.611000.106701.441035.70963.44
    8BOAmビリヤード最適化アルゴリズム M0.957570.825990.252352.035901.000000.900360.305022.205380.735380.525230.095631.356255.59862.19
    9AAmアーチェリーアルゴリズム M0.917440.708760.421602.047800.925270.758020.353282.036570.673850.552000.237381.463235.54861.64
    10ESG社会集団の進化(joo)0.999060.796540.350562.146161.000000.828630.131021.959650.823330.553000.047251.423585.52961.44
    11SIA等方的焼きなまし(joo)0.957840.842640.414652.215130.982390.795860.205071.983320.686670.493000.090531.270205.46960.76
    12EOm極値最適化M0.761660.772420.317471.851550.999990.767510.235272.002770.747690.539690.142491.429875.28458.71
    13BBO生物地理学に基づく最適化0.949120.694560.350311.993990.938200.673650.256821.868670.746150.482770.173691.402615.26558.50
    14ACS人工協調探索0.755470.747440.304071.806981.000000.888610.224132.112740.690770.481850.133221.305835.22658.06
    15DA弁証法的アルゴリズム0.861830.700330.337241.899400.981630.727720.287181.996530.703080.452920.163671.319675.21657.95
    16BHAmブラックホールアルゴリズム M0.752360.766750.345831.864930.935930.801520.271772.009230.650770.516460.154721.321955.19657.73
    17ASO無政府社会最適化0.848720.746460.314651.909830.961480.791500.238031.991010.570770.540620.166141.277525.17857.54
    18RFOロイヤルフラッシュ最適化(joo)0.833610.737420.346291.917330.894240.738240.240981.873460.631540.502920.164211.298675.08956.55
    19AOSm原子軌道探索 M0.802320.704490.310211.817020.856600.694510.219961.771070.746150.528620.143581.418355.00655.63
    20TSEA亀甲進化アルゴリズム(joo)0.967980.644800.296721.909490.994490.619810.227081.841390.690770.426460.135981.253225.00455.60
    21BSAバックトラッキング探索アルゴリズム0.973090.545340.290981.809410.999990.585430.217471.802890.847690.369530.129781.347004.95955.10
    22DE差分進化0.950440.616740.303081.870260.953170.788960.166521.908650.786670.360330.029531.176534.95555.06
    23SRAレストラン経営達人アルゴリズム(joo)0.968830.634550.292171.895550.946370.555060.191241.692670.749230.440310.125261.314804.90354.48
    24CRO化学反応最適化0.946290.661120.298531.905930.879060.584220.211461.674730.758460.426460.126861.311784.89254.36
    25BIO血液型遺伝最適化(joo)0.815680.653360.308771.777810.899370.653190.217601.770160.678460.476310.139021.293784.84253.80
    26BSA群鳥アルゴリズム0.893060.649000.262501.804550.924200.711210.249391.884790.693850.326150.100121.120124.80953.44
    27DEAイルカエコーロケーションアルゴリズム0.759950.675720.341711.777380.895820.642230.239411.777460.615380.440310.151151.206844.76252.91
    28HSハーモニーサーチ0.865090.687820.325271.878180.999990.680020.095901.775920.620000.422670.054581.097254.75152.79
    29SSG苗木の播種と育成0.778390.649250.395431.823080.859730.624670.174291.658690.646670.441330.105981,193984.67651.95
    30BCOm細菌走化性最適化 M0.759530.622680.314831.697040.893780.613390.225421.732590.653850.420920.144351.219124.64951.65
    31ABOアフリカ水牛最適化0.833370.622470.299641.755480.921700.586180.197231.705110.610000.431540.132251,173784.63451.49
    32(PO)ES(PO) 進化戦略0.790250.626470.429351.846060.876160.609430.195911.681510.590000.379330.113221.082554.61051.22
    33FBAフラクタルベースアルゴリズム0.790000.651340.289651.730990.871580.568230.188771.628580.610770.460620.123981,195374.55550.61
    34TSmタブーサーチM0.877950.614310.291041.783300.928850.518440.190541.637830.610770.382150.121571.114494.53650.40
    35BSOブレインストーム最適化0.937360.576160.296881.810410.931310.558660.235371.725340.552310.290770.119140.962224.49849.98
    36WOAmクジラ最適化アルゴリズム M0.845210.562980.262631.670810.931000.522780.163651.617430.663080.411380.113571.188034.47649.74
    37AEFA人工電界アルゴリズム0.877000.617530.252351.746880.927290.726980.180641.834900.666150.116310.095080.877544.45949.55
    38AEO人工生態系ベースの最適化アルゴリズム0.913800.467130.264701.645630.902230.437050.214001.553270.661540.308000.285631.255174.45449.49
    39CAmラクダアルゴリズムM0.786840.560420.351331.698590.827720.560410.243361.631490.648460.330920.134181.113564.44449.37
    40ACOm蟻コロニー最適化M0.881900.661270.303771.846930.858730.586800.150511.596040.596670.373330.024720.994724.43849.31
    41CMAES共分散行列適応進化戦略0.762580.720890.000001.483470.820560.796160.000001.616720.758460.490770.000001.249234.34948.33
    42DA_duelistduelist_algorithm0.927820.537780.277921.743520.869570.475360.181931.526860.621530.335690.117151.074374.34548.28
    43BFO-GA細菌採餌最適化 - GA0.891500.551110.315291.757900.969820.396120.063051.428990.726670.275000.035251.036924.22446.93
    44SOAシンプル最適化アルゴリズム0.915200.469760.270891.655850.896750.374010.169841.440600.695380.280310.108521.084224.18146.45
    45ABHA人工蜂群アルゴリズム0.841310.542270.263041.646630.878580.477790.171811.528180.509230.338770.103970.951974.12745.85
    RWランダムウォーク0.487540.321590.257811.066940.375540.219440.158770.753750.279690.149170.098470.527342.34826.09


    まとめ

    決闘者アルゴリズムは、メタヒューリスティック最適化の分野で良好な結果を示しており、集団ベースのアルゴリズムの中で上位45位以内にランクされています。 最適化手法の中で絶対的な首位を目指すものではありませんが、最大の強みは処理速度にあります。

    適応的な学習戦略:取引システムの最適化において、これは、性能の低いパラメータ設定が、性能の高い設定のパターンを迅速に取り入れることを意味します。これは、非定常な金融時系列を扱う際に特に有効です。

    活用と探索のバランス:勝者も現状に満足することなく、革新を通じてさらなる改善を目指します。 これは、変化する市場環境に適応しなければならない取引戦略にとって、非常に重要です。

    制御されたランダム性:この要素によって、市場の不確実性を自然にモデル化できます。優れた戦略であっても、短期的なボラティリティの影響で、一時的に良好な成績を収められないことがあります。

    チャンピオンを中心とした階層構造:優れた解を保持し、その特性を他の個体に伝播させます。取引に置き換えると、実績のある戦略を維持しながら、新たな収益機会を同時に追求することに相当します。

    決闘者アルゴリズムは、武術の比喩を通じて、シンプルながらも効果的な考え方を最適化の世界にもたらす、堅実な手法です。すなわち、優れた相手から学び、優位な立場で新たに挑戦し、次の世代に知識を伝えるという考え方です。

    タブ

    図2: 各テストにおけるアルゴリズムの成績を色の濃淡で表現した図

    グラフ

    図3: アルゴリズムのテスト結果を示すヒストグラム(0~100のスケール。値が大きいほど良好で、100は理論上の最大値。アーカイブには、ランキング表を計算するためのスクリプトが含まれています)

    DA_duelistの長所と短所:

    長所:

    1. 速い。
    2. テスト関数間での成績のばらつきが小さい。

    短所:

    1. 収束精度が低い。

    アルゴリズムの最新バージョンのソースコードを含むアーカイブが、記事に添付されています。 本記事の著者は、標準的なアルゴリズムの説明が完全に正確であることを保証するものではありません。 探索性能を向上させるため、多くのアルゴリズムに変更が加えられています。 記事内で示されている結論や評価は、実験結果に基づいています。


    本記事で使用したプログラム

    #名前種類説明
    1#C_AO.mqh
    Include
    集団最適化アルゴリズムの親クラス
    2#C_AO_enum.mqh
    Include
    集団最適化アルゴリズムの列挙型
    3TestFunctions.mqh
    Include
    テスト関数ライブラリ
    4
    TestStandFunctions.mqh
    Include
    テスト環境用関数ライブラリ
    5
    Utilities.mqh
    Include
    補助関数ライブラリ
    6
    CalculationTestResults.mqh
    Include
    比較表の結果を計算するスクリプト
    7
    Testing AOs.mq5
    スクリプトすべての集団最適化アルゴリズムに共通のテスト環境
    8
    Simple use of population optimization algorithms.mq5
    スクリプト
    可視化機能を使わずに集団最適化アルゴリズムを使用する簡単な例
    9
    Test_AO_DA_duelist.mq5
    スクリプトDA_duelistのテスト環境

    MetaQuotes Ltdによってロシア語から翻訳されました。
    元の記事: https://www.mql5.com/ru/articles/19093

    添付されたファイル |
    DAvduelistk.zip (262.07 KB)
    最後のコメント | ディスカッションに移動 (2)
    Александр Бельмецов
    Александр Бельмецов | 15 8月 2025 において 04:00
    MetaQuotes:

    「決闘者のアルゴリズム — Duelist Algorithm」という記事が公開されました:

    著者:Andrey Dik

    リングで戦う決闘者たち。市場はリングではなく、地形そのものです。ある時は平地、ある時は腰まで泥沼に浸かることもあります。氷の上ではフィギュアスケーターが勝つでしょうが、泥沼では……結局、どんな戦士が生まれるのでしょうか?
    lynxntech
    lynxntech | 15 8月 2025 において 04:21
    Александр Бельмецов #:
    リングで戦う決闘者たち。市場はリングではなく、ある種の「戦場」だ。時には夏場、時には腰まで泥沼に浸かることもある。氷の上ならフィギュアスケーターが勝つが、泥沼では……結局、どんな戦士になるというのか?

    インターネットで自分で調べてみてください。これはこのトピックの投稿者がでっち上げた話ではありません。

    取引におけるニューラルネットワーク:Temporal Queryモデル(TQNet) 取引におけるニューラルネットワーク:Temporal Queryモデル(TQNet)
    TQNetフレームワークは、モジュール性、柔軟性、高い処理性能を兼ね備え、金融時系列データのモデリングと予測に新たな可能性を切り開きます。 高度なパラメータ初期化手法も含め、大域的な相関関係を捉えるための複雑なメカニズムを実装する可能性について考察します。
    取引におけるニューラルネットワーク:構造成分の分離(最終回) 取引におけるニューラルネットワーク:構造成分の分離(最終回)
    SCNNアーキテクチャについて詳しく解説するとともに、MQL5を用いた実装方法の一例を紹介します。 特に、時系列分解をニューラルネットワークの手法やAttention機構と組み合わせる方法について説明します。
    エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
    この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
    取引におけるニューラルネットワーク:構造成分の分離(Encoder) 取引におけるニューラルネットワーク:構造成分の分離(Encoder)
    柔軟性と解釈可能性を兼ね備え、時系列データの構造成分を精密に特定できるSCNN(Structured Component Neural Network:構造成分ニューラルネットワーク)フレームワークの実装に向けた次のステップを解説します。 市場環境の変化に対するモデルの頑健性を支える、適応的正規化とAttention機構の仕組みについて詳しく解説します。