English Русский 中文 Español Deutsch Português
preview
人工原子アルゴリズム(A3)

人工原子アルゴリズム(A3)

MetaTrader 5トレーディングシステム |
15 7
Andrey Dik
Andrey Dik

内容

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


はじめに

アルゴリズム取引において重要な課題の1つが、取引戦略のパラメータ最適化です。トレーダーは日々、さまざまな変数を調整する必要があります。たとえば、インジケータの期間、ストップロスやテイクプロフィットのレベル、ポジションサイズ、時間フィルター、その他にも数多くのパラメータがあります。これらのパラメータの組み合わせを少し変えるだけでも、戦略のパフォーマンスは大きく変化する可能性があります。そのため私たちは、妥当な時間内で最適な解を見つけることができる、より効果的なアルゴリズムを探し続けています。

この記事では、もう1つの最適化アルゴリズムを取り上げます。それが人工原子アルゴリズム(A3, Artificial Atom Algorithm)です。A3は、化学反応から着想を得たメタヒューリスティック最適化アルゴリズムです。このアルゴリズムはトルコの研究者によって開発され、2018年に初めて学術コミュニティへ発表されました。


アルゴリズムの実装

このアルゴリズムに取り組んでいる際、元の論文の説明にはいくつか曖昧な点があることに気づきました。一部の演算子については十分な詳細が示されておらず、そのため実際に実装することが難しくなっています。そこで、ここではアルゴリズムについて原著で示されている説明を紹介します。ただし、実際の実装については、私自身の判断でおこなっています。具体的には、これまでの経験と、この種のアルゴリズムに特徴的な構造上の原則をもとに実装しました。これは原著における大きな不足点です。そのため、実装する開発者にはかなり広い解釈の余地が残されています。さらに、作業を進める中でベースクラスにも変更を加えました。この点についても、アルゴリズムの基本的な実装を説明した後で取り上げます。

このアルゴリズムでは、最適解を見つけるために、原子と電子の相互作用をモデル化します。基本となる構成要素は原子です。原子は、問題に対する潜在的な解を表します。電子は意思決定変数を表します。そして、共有結合は最良の解を保存し、複製するための演算子として機能します。一方、イオン結合は探索空間を探索し、大域最適解を見つけるための演算子として機能します。 

アルゴリズムは、まず原子の集合をランダムに生成するところから始まります。次に、目的関数を使用して、それぞれの原子の品質を評価します。その後、共有結合およびイオン結合の演算子を適用して、解を改善します。残念ながら、これを具体的にどのようにおこなうのかについては説明されていません。次に、電子の影響を評価する必要があります。しかし、ここでも、その方法は明確ではありません。さらに、電子と原子をソートする必要があります。原子、つまり解をソートすることはできます。しかし、電子をどのようにソートすればよいのでしょうか。これは謎です。まるで人の手足を、腕や脚、右左で無理に並べ替えようとするようなもので、意味がありません。停止条件に達するまで、このプロセスを繰り返し実行します。とはいえ、ことわざにもあるように、手元にあるもので取り組むしかありません。そこで、著者のアイデアを可能な限り反映しながら、実際に動作するアルゴリズムを組み立ててみる必要があります。

ここでは、私自身が最終的に修正したA3アルゴリズムの擬似コードを紹介します。

初期化入力パラメータ

popSize:原子数(デフォルトは10)
covalentRate:共有結合係数(デフォルトは0.1)
rangeMin []、rangeMax []:各変数の検索範囲
rangeStep []:離散化ステップ幅

事前計算

エリート原子の数を計算します。
covalentCount = floor (popSize × covalentRate)
制限値:最小1、最大(popSize - 1)

popSize個の原子からなる集団を作成します。
各原子について、以下を初期化します。
座標c []
局所最悪解cW []
適応度f、最悪適応度fW


基本最適化ループ
ステップ1:最初の反復処理(revision = falseの場合)
各原子i(0からpopSize-1まで)について:
    各座標j(0からcoords-1まで)について:
        [rangeMin [j], rangeMax [j]]の範囲内でランダムな値を生成します。
        rangeStep [j]に従って離散化します。
        結果をa [i].c [j]に保存します。
    
revision = trueに設定します。
反復処理を終了します。

ステップ2:原子位置の更新(移動)
各原子i(0からpopSize-1まで)について:
    
    i ≤ covalentCountの場合(原子がエリートの場合):
        各座標c(0からcoords-1まで)について:
            
            random() < covalentRate の場合(10%の確率):
                // 大域最良解に向かって移動
                step = random() × (cB [c] - a [i].c [c]) × covalentCount
                a [i].c [c] = a [i].c [c] + step
            
            それ以外の場合(90%の確率):
                // 最良解を中心としてPowerDistributionで生成
                a[i]. c[c] = PowerDistribution(center=cB [c], min=rangeMin [c], max=rangeMax [c], degree=20)
            その後、a [i].c [c]に対して離散化を適用します。
    
    それ以外の場合(原子がエリートではない場合):
        各座標c(0からcoords-1まで)について:
            // ランダムなエリート原子をサンプルとして選択
            ind = random_integer(0, covalentCount)
            
            // 局所最悪解からエリート原子の位置へ移動
            direction = a [ind].c [c] - a [i].cW [c]
            step = random() × direction × (1.0 - covalentCount)
            a [i].c [c] = a [i].c [c] + step
            その後、a [i].c [c]に対して離散化を適用します。

ステップ3:適応度の計算
各原子iについて:
    目的関数を使用してa [i].fの適応度を計算します。

ステップ4:メモリの更新とソート(Revision)
// 局所最悪解を更新
各原子i(0からpopSize-1まで)について:
    IF a [i].f < a [i].fW:
        a [i].fW = a [i].f
        a [i].cW に a [i].c をコピー
        大域最悪値fWを更新

// 適応度の降順で集団をソート
原子配列a[]を、フィールドfに基づいてソートします。

// 大域最良解を更新
IF a [0].f > fB:
    fB = a [0].f
    a [0].c を cB にコピー

// 大域最悪値を更新
IF a [popSize-1].f < fW:
    fW = a [popSize-1].f
    a [popSize - 1].c を cW にコピー

ステップ5:停止条件の確認
最大反復回数に達した場合:
    アルゴリズムを終了
まだ最大反復回数に達していない場合:
    ステップ2に戻る

ここからは、実際のコードによる実装へ進みましょう。原子とその結合というメタファーに基づく人工原子アルゴリズムの実装を表すクラスを作成します。このクラスはC_AOクラスを継承し、A3アルゴリズムのコンテキストに合わせて、その基本機能を拡張します。

パラメータ設定メソッド:SetParams()関数は、パラメータ配列から現在の値を読み取り、それぞれ対応する変数へ保存します。これにより、アルゴリズムを実行する前に、柔軟にパラメータを設定できるようになります。初期化:Init()メソッドは、実行に必要なパラメータと構造を準備します。ここでは、各変数の値の範囲とステップ幅、およびエポック数を受け取ります。アルゴリズムの主要な処理:Moving()メソッドとRevision()メソッドによって、システム内の原子の移動と更新のフェーズを実装します。これは、最適な解を探索するための各ステージに対応しています。以下はパラメータと変数です。

  • popSize:原子集団サイズ(集団内の要素数)
  • covalentRate:原子間の結合形成を表す係数
  • covalentCount:結合形成に関与する原子の内部的な数(計算処理で内部的に使用)

このクラスの全体的な目的は、原子同士の結合を考慮しながら、原子システムの挙動をモデル化することです。そのために、各種パラメータと処理メソッドを使用します。そして、これらの概念に基づいて探索および最適化のロジックを実装していきます。

//————————————————————————————————————————————————————————————————————
class C_AO_A3 : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_A3 () { }
  C_AO_A3 ()
  {
    ao_name = "A3";
    ao_desc = "Artificial Atom Algorithm";
    ao_link = "https://www.mql5.com/ja/articles/18958";

    popSize      = 10;    // number of atoms (m)
    covalentRate = 0.1;   // covalent bond coefficient (β)

    ArrayResize (params, 2);

    params [0].name = "popSize";      params [0].val = popSize;
    params [1].name = "covalentRate"; params [1].val = covalentRate;
  }

  void SetParams ()
  {
    popSize      = (int)params [0].val;
    covalentRate = params      [1].val;
  }

  bool Init (const double &rangeMinP  [],  // minimum values
             const double &rangeMaxP  [],  // maximum values
             const double &rangeStepP [],  // step change
             const int     epochsP = 0);   // number of epochs

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double covalentRate;       // covalent bond coefficient (β)

  private: //---------------------------------------------------------
  int    covalentCount;      // number of atoms for covalent bond
};
//————————————————————————————————————————————————————————————————————

Init()メソッドは、アルゴリズムを動作させるための準備をおこなう重要なステップです。主な役割は、最適化を開始する前に、必要となるすべてのパラメータと構造を初期化することです。このメソッドは、次の4つのパラメータを入力として受け取ります。

  1. rangeMinP []:アルゴリズムによって最適化される各変数の最小値。
  2. rangeMaxP []:各変数の最大値。
  3. rangeStepP []:各変数の変化ステップ。このステップを使用して値を離散化し、探索精度を決定します。
  4. epochsP:アルゴリズムを実行するエポック数。

まず、基底クラスC_AOのStandardInit()メソッドが呼び出されます。このメソッドは、すべての最適化アルゴリズムに共通する初期化処理を実行します。StandardInit()が正常に完了すると、次にArtificial Atom Algorithm(人工原子アルゴリズム)固有のパラメータを計算します。特に重要なのが、covalentCountの計算です。これは、共有結合に参加する原子の数を表します。計算式は「covalentCount = popSize × covalentRate」のようになります。計算結果はMathFloorを使用して切り捨てます。原子の数は整数でなければならないためです。その後、covalentCountの値が論理的に正しい範囲に収まっているかどうかを確認します。すべての初期化処理が正常に完了すると、このメソッドはtrueを返します。これは、アルゴリズムを実行する準備が整ったことを示します。

このように、Init()メソッドはArtificial Atom Algorithmを実行可能な状態へ完全に準備します。つまり、最適化アルゴリズムに共通するパラメータだけでなく、今回の「原子」と「結合」というモデル固有のパラメータについても初期化をおこないます。

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

  //------------------------------------------------------------------
  covalentCount = (int)MathFloor (popSize * covalentRate);
  if (covalentCount < 1) covalentCount = 1;
  if (covalentCount >= popSize) covalentCount = popSize - 1;

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

C_AO_A3クラスのMoving()メソッドは、Artificial Atom Algorithm(人工原子アルゴリズム)における主要な処理の1つです。このメソッドでは、探索空間内における原子の「移動」、つまり原子の位置を変更する処理を実装します。これは、最適な解を見つけることを目的として、原子の集団に進化的な変化を発生させる処理をモデル化したものです。Moving()メソッドが呼び出されるたびに、最初にrevisionの状態を確認します。このフラグは、集団がすでに初期化されているかどうかを示します。

初期化処理(revisionがfalseの場合):Moving()メソッドが初めて呼び出されたか、あるいは状態がリセットされた後で、原子の集団がまだ初期化されていないことを意味します。この場合、以下の処理をおこないます。まず、popSize(原子の数)についてループし、さらにcoords(探索空間の次元数)についてもループします。各原子の各座標について、指定された範囲内からランダムな値を生成し、その位置を初期化します。最初の呼び出しによる初期化が完了すると、revisionをtrueに設定します。これにより、次回以降にMoving()が呼び出されたときには、再び初期化するのではなく、メインとなる移動処理が実行されます。そして、最初の呼び出しでおこなうべき処理は完了したため、メソッドを終了します。

原子を移動させる基本処理(revisionがtrueの場合):集団の初期化が完了すると、Moving()メソッドは原子の位置を更新し始めます。これは、原子が探索空間内を移動する様子をシミュレートする処理です。ここでは、iのインデックスとcovalentCountの値に応じて、原子を2つのグループに分けます。 

グループ1:共有結合に参加する原子(i <= covalentCount) このグループに属する各原子について、それぞれの座標を処理します。まず、ランダムな値を生成します。この値が共有結合係数covalentRateより小さい場合、原子の座標を、現在の最良解であるcB[c]との引力や相互作用をシミュレートする式に従って変更します。ここでcBは、集団内でこれまでに見つかった最良解の座標を表しており、この計算ではcovalentCountも考慮されます。

一方、ランダムな値がcovalentRate以上の場合、原子の座標をu.PowerDistribution関数を使用して変更します。この関数は、cB[c]付近に偏った値を生成しますが、ある程度のランダムなばらつきも持たせます。これにより、よりランダムな探索、あるいは「散乱」をシミュレートします。

座標がどちらの方法で変更された場合でも、最後にu.SeInDiSp関数で再度処理します。これによって、座標が許容される範囲内に収まり、指定された離散グリッド上の値になるように調整されます。 

グループ2:共有結合に参加しない原子(i > covalentCount):このグループに属する各原子についても、それぞれの座標を処理します。まず、0からcovalentCountまでの範囲から、ランダムなインデックスindを選択します。つまり、「結合していない」原子は、共有結合に参加している原子のうち1つと相互作用することになります。

その後、選択された「結合している」原子と、その原子が以前に保持していた「最良」の位置との相互作用、あるいは反発をシミュレートするように、対象原子の座標を変更します。変更後の座標は、再びu.SeInDiSpによって処理され、指定された制約を満たすように調整されます。

Moving()メソッドは、探索処理の中心となるメソッドです。物理モデルから着想を得た確率的なルールによって、原子の位置を更新します。「共有結合」に参加している原子は、見つかった最良解へ向かって移動する傾向を持ちます。一方、それ以外の原子は、共有結合に参加している原子との相互作用を利用しながら、探索空間をより広く探索します。

//————————————————————————————————————————————————————————————————————
//--- The main step of the algorithm
void C_AO_A3::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;
  }

  //------------------------------------------------------------------
  int    ind = 0;

  for (int i = 0; i < popSize; i++)
  {
    if (i <= covalentCount)
    {
      for (int c = 0; c < coords; c++)
      {
        if (u.RNDprobab () < covalentRate)
        {
          a [i].c [c] = a [i].c [c] + u.RNDprobab () * (cB [c] - a [i].c [c]) * covalentCount;//(1.0 - covalentCount);
        }
        else
        {
          a [i].c [c] = u.PowerDistribution (cB [c], rangeMin [c], rangeMax [c], 20);
        }
        a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
    else
    {
      for (int c = 0; c < coords; c++)
      {
        ind = u.RNDintInRange (0, covalentCount);

        a [i].c [c] = a [i].c [c] + u.RNDprobab () * (a [ind].c [c] - a [i].cW [c]) * (1.0 - covalentCount);//covalentCount;
        a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
  }
}
//————————————————————————————————————————————————————————————————————

C_AO_A3クラスのRevision()メソッドは、アルゴリズムの現在の反復において、原子の集団から見つかった最良解と最悪解に関する情報を更新する役割を担います。これは重要なステップです。なぜなら、これらの「最良」と「最悪」の値(集団全体のものと、各原子に個別に設定されるものの両方)が、次のステップでの探索を導くために使用されるからです。各原子について、個別の最悪の局所解を更新します。u.Sorting()関数が呼び出され、fの適応度に基づいて、a原子の集団全体をソートします。

ソート後、a[0]は現在の集団における最良の原子を表します。その後、fBをa[0].fの値で更新します。a[0].cの座標をcB(大域最良座標)にコピーします。a[popSize - 1]は、ソート後の現在の集団における最悪の原子を表します。a[popSize - 1].fの適応度を、これまでに見つかった大域最悪適応度であるfWと比較します。a[popSize - 1].fが実際により悪い場合は、fWをa[popSize - 1].fの値で更新し、a[popSize - 1].cの座標をcW(大域最悪座標)にコピーします。

Revision()メソッドは、最適化の進行状況を体系的に監視します。各エージェント(原子)の個別の「最良」状態と、集団全体の「最良」および「最悪」の状態の両方を更新します。 

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

  static S_AO_Agent aT []; ArrayResize (aT, popSize);
  u.Sorting (a, aT, popSize);

  if (a [0].f > fB)
  {
    fB = a [0].f;
    ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY);
  }

  if (a [popSize - 1].f < fW)
  {
    fW = a [popSize - 1].f;
    ArrayCopy (cW, a [popSize - 1].c, 0, 0, WHOLE_ARRAY);
  }
}
//————————————————————————————————————————————————————————————————————

結果として得られた実装は、かなりコンパクトです。それでは次に、すべての最適化アルゴリズムが継承する基底クラスに進みましょう。先ほど述べたように、このクラスには変更が加えられていますので、このクラスについて説明します。

アルゴリズムのパラメータを保存するためのシンプルな構造体です。これにより、あらゆる最適化アルゴリズムの設定を動的に管理できます。

//——————————————————————————————————————————————————————————————————————————————
struct S_AlgoParam
{
    double val;
    string name;
};
//——————————————————————————————————————————————————————————————————————————————

S_AO_Agent構造体は、最適化問題に対する潜在的な解を表す1つの「エージェント」を表し、多次元の探索空間を移動するためのものです。S_AO_Agent構造体には、いくつかのフィールドが含まれており、それらはいくつかのカテゴリーに分けることができます。1. 座標(探索空間内の位置):

  • c:現在のエージェントの座標。現在のエージェントの位置を保存する主要な配列です。
  • cP:エージェントの以前の座標。エージェントの移動を追跡するために使用します。
  • cB:このエージェントがこれまでに到達した最良の座標。この情報により、エージェントは最も成功した位置を「記憶」し、それらの位置へ戻ったり、以降の探索に利用したりできます。
  • cW:エージェントの最悪の座標。

2.適応度(解の品質):

  • f:現在のエージェントの適応度。現在のc座標に対して計算された目的関数の値です。
  • fP:エージェントの以前の適応度。cPと同様に、適応度の変化を追跡するために使用します。
  • fB:このエージェントがこれまでに達成した最良の適応度。cBの座標に対応します。
  • fW:エージェントの最悪の適応度。cWの座標に対応します。

3. 補助データ:cnt:整数カウンタ。 

この構造体には、エージェントを初期化するためのpublicなInit()メソッドも含まれています。このメソッドは、探索空間の次元数、つまりエージェントが持つ座標の数を指定するcoordsという整数パラメータを1つ受け取ります。

Init()が呼び出されると、次の処理がおこなわれます。座標配列(c、cP、cB、cW)のサイズが、指定されたcoordsに合わせて動的に変更されます。これにより、各エージェントが対象となる問題を処理するために必要な正しい次元数を持つことが保証されます。適応度の値(f、fP、fB、fW)も初期化されます。f、fP、fBには-DBL_MAX(可能な最小のdouble値)が設定され、fWにはDBL_MAX(可能な最大値)が設定されます。これは、最大値を探索する場合の標準的な方法です。f、fP、fBについては、最初に取得される値が初期値より大きくなる可能性がほぼ常にあります。一方、fW(最悪値)については最大値を設定しておくことで、最初に取得されたどのような値でも、より良い値(より小さい値)として扱うことができます。cntカウンタはゼロに初期化されます。

全体として、S_AO_Agentは進化型および集団ベースの最適化アルゴリズムにおける、集団の各メンバーを扱うための包括的なデータ構造を提供します。これにより、各エージェントの現在の状態だけでなく、これまでの成功と失敗の履歴も追跡できます。

//——————————————————————————————————————————————————————————————————————————————
struct S_AO_Agent
{
    double c  []; //coordinates
    double cP []; //previous coordinates
    double cB []; //best coordinates
    double cW []; //worst coordinates

    double f;     //fitness
    double fP;    //previous fitness
    double fB;    //best fitness
    double fW;    //worst fitness

    int    cnt;   //counter

    void Init (int coords)
    {
      ArrayResize (c,  coords);
      ArrayResize (cP, coords);
      ArrayResize (cB, coords);
      ArrayResize (cW, coords);

      f  = -DBL_MAX;
      fP = -DBL_MAX;
      fB = -DBL_MAX;
      fW =  DBL_MAX;

      cnt = 0;
    }
};
//——————————————————————————————————————————————————————————————————————————————

基底クラスであるC_AOを見てみましょう。C_AOクラスの全体構造。Publicメンバー。これらは、クラスの外部からアクセスできるフィールドとメソッドです。 

最良/最悪解の全体的な状態を表すデータ

  • cB []:アルゴリズム全体で見つかった大域最良解の座標配列
  • cW []:アルゴリズム全体で見つかった大域最悪解の座標配列
  • fB:大域最良解の適応度(cBに相当)
  • fW:大域最悪解の適応度(cWに相当)

ここで、これらは大域最良/最悪解であり、S_AO_Agent内部にあるfB、fWは各エージェント固有のものであることに注意してください。 

集団およびパラメータ関連のデータ

  • a []:S_AO_Agentオブジェクトの配列。これは「集団」であり、それぞれが潜在的な解を表します。
  • params []:S_AlgoParamオブジェクトの配列。ここには、アルゴリズム固有のパラメータ(影響係数、確率など)が保存され、設定できます。
  • revision:状態の確認と更新が必要かどうかを示すフラグ。 
仮想メソッド(派生クラスで実装が必要) :これらのメソッドはvirtualとして宣言されており、派生クラスでオーバーライドして、それぞれのアルゴリズム固有のロジックを実装できます。
  • SetParams():アルゴリズム固有のパラメータを設定するメソッド。派生クラスでは、params配列に値を設定します。
  • Init():アルゴリズムを初期化するためのメインメソッド。探索範囲(最小値、最大値、ステップ)とエポック数を受け取り、初期化に成功するとtrueを返します。
  • Moving ():探索空間内でエージェントを「移動」または「進化」させるロジックを実装するメソッド。アルゴリズムの中心となる処理です。
  • Revision ():アルゴリズムの状態を「確認」または「更新」するフェーズを実装するメソッド。
  • Injection():特定のエージェントの座標に、指定された値を「注入」するためのメソッド。 

探索空間のパラメータ:探索空間に関する値を保存する配列

  • rangeMin []:探索空間の各座標における最小値
  • rangeMax []:探索空間の各座標における最大値
  • rangeStep []:各座標のステップ幅(離散的な探索空間や、探索の粒度を決定する場合に使用します)

アルゴリズムの内部パラメータ

  • coords:探索空間の次元数(座標数)
  • popSize:アルゴリズムにおける集団サイズ(エージェントの数)

補助ユーティリティ:u:乱数生成や数学的演算などの共通関数を含む補助クラスのオブジェクト。

標準初期化メソッド(protected):StandardInit()は、すべての派生アルゴリズムに共通する標準的な初期化処理を実行するためのメソッドです。まず、現在のシステム時刻をシードとして乱数生成器を初期化します。次に、大域最良/最悪解を追跡するため、fBを負の無限大、fWを正の無限大に設定します。さらに、revisionフラグをfalseに設定します。その後、座標数(探索空間の次元数)を確認して設定します。内部配列であるrangeMin、rangeMax、rangeStep、cB、cWのサイズを座標数に合わせて変更します。また、集団サイズpopSizeに合わせてaエージェント配列のサイズを変更し、それぞれのエージェントについて自身のInit()メソッドを呼び出し、エージェント内部の座標配列を初期化します。続いて、入力された探索範囲を内部変数へコピーします。初期化に成功するとtrueを返し、入力された範囲配列のサイズが一致しないなどのエラーが発生した場合はfalseを返します。

C_AOクラスは、具体的な最適化アルゴリズムを実装するためのベースクラスとして機能します。新しいアルゴリズム(AOの具体的なバージョン)を作成する場合は、C_AOを継承し、SetParams()、Init()、Moving()、Revision()、Injection()の各Virtualメソッドをオーバーライドして、そのアルゴリズム固有のロジックを実装します。一方、StandardInit()を使用することで、すべてのアルゴリズムに共通する初期化処理のコードが重複することを避けられます。

//——————————————————————————————————————————————————————————————————————————————
class C_AO
{
  public: //--------------------------------------------------------------------
  C_AO () { }
  ~C_AO () { }

  double      cB     []; //best coordinates
  double      cW     []; //worst coordinates
  double      fB;        //FF of the best coordinates
  double      fW;        //FF of the worst coordinates
  S_AO_Agent  a      []; //agents
  S_AlgoParam params []; //algorithm parameters
  bool        revision;

  virtual void SetParams () { }
  virtual bool Init (const double &rangeMinP  [], //minimum search range
                     const double &rangeMaxP  [], //maximum search range
                     const double &rangeStepP [], //step search
                     const int     epochsP = 0)   //number of epochs
  { return false;}

  virtual void Moving    () { }
  virtual void Revision  () { }
  virtual void Injection (const int popPos, const int coordPos, const double value) { }

  string GetName   () { return ao_name;}
  string GetDesc   () { return ao_desc;}
  string GetLink   () { return ao_link;}
  string GetParams ()
  {
    string str = "";
    for (int i = 0; i < ArraySize (params); i++)
    {
      str += (string)params [i].val + "|";
    }
    return str;
  }


  protected: //-----------------------------------------------------------------
  string ao_name;      //ao name;
  string ao_desc;      //ao description
  string ao_link;      //ao link

  double rangeMin  []; //minimum search range
  double rangeMax  []; //maximum search range
  double rangeStep []; //step search

  int    coords;       //coordinates number
  int    popSize;      //population size

  C_AO_Utilities u;     //auxiliary functions

  bool StandardInit (const double &rangeMinP  [], //minimum search range
                     const double &rangeMaxP  [], //maximum search range
                     const double &rangeStepP []) //step search
  {
    int seed = (int)GetTickCount64 ();
    MathSrand (seed); //reset of the generator

    fB       = -DBL_MAX;
    fW       =  DBL_MAX;
    revision =  false;

    coords  = ArraySize (rangeMinP);
    if (coords == 0 || coords != ArraySize (rangeMaxP) || coords != ArraySize (rangeStepP)) return false;

    ArrayResize     (rangeMin,  coords);
    ArrayResize     (rangeMax,  coords);
    ArrayResize     (rangeStep, coords);
    ArrayResize     (cB,        coords);
    ArrayResize     (cW,        coords);

    ArrayResize (a, popSize);
    for (int i = 0; i < popSize; i++) a [i].Init (coords);

    ArrayCopy (rangeMin,  rangeMinP,  0, 0, WHOLE_ARRAY);
    ArrayCopy (rangeMax,  rangeMaxP,  0, 0, WHOLE_ARRAY);
    ArrayCopy (rangeStep, rangeStepP, 0, 0, WHOLE_ARRAY);

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


テスト結果

ここでは、A3アルゴリズムがどのように動作するのかを見てみましょう。小規模な集団における平均的な結果です。

A3|Artificial Atom Algorithm|30.0|0.2|
=============================
5 Hilly's; Func runs:10000; result:0.7074464987418227
25 Hilly's; Func runs:10000; result:0.49461443027863367
500 Hilly's; Func runs:10000; result:0.27674325929370214
=============================
5 Forest's; Func runs:10000; result:0.8910275237236067
25 Forest's; Func runs:10000; result:0.43888040941642725
500 Forest's; Func runs:10000; result:0.17553299655770818
=============================
5 Megacity's; Func runs:10000; result:0.5323076923076923
25 Megacity's; Func runs:10000; result:0.3270769230769231
500 Megacity's; Func runs:10000; result:0.11430769230769337
=============================
All score:3.95794 (43.98%)

低次元関数に対する結果の散らばりが可視化されています(緑色の線)。ただし、解が探索空間を十分にカバーしている点は注目に値します。

Hilly

Hillyテスト関数のA3

Forest

Forestテスト関数のA3

Megacity

Megacityテスト関数のA3

評価表では、A3アルゴリズムを参考情報としてのみ掲載しています。

# AO 説明 Hilly Hilly
ファイナル
Forest Forest
ファイナル
Megacity(離散) Megacity
ファイナル
ファイナル
結果

MAX
10p(5F) 50p(25F) 1000p(500F) 10p(5F) 50p(25F) 1000p(500F) 10p(5F) 50p(25F) 1000p(500F)
1 ANS across neighbourhood search 0.94948 0.84776 0.43857 2.23581 1.00000 0.92334 0.39988 2.32323 0.70923 0.63477 0.23091 1.57491 6.134 68.15
2 CLA コードロックアルゴリズム(joo) 0.95345 0.87107 0.37590 2.20042 0.98942 0.91709 0.31642 2.22294 0.79692 0.69385 0.19303 1.68380 6.107 67.86
3 AMOm 動物移動最適化m 0.90358 0.84317 0.46284 2.20959 0.99001 0.92436 0.46598 2.38034 0.56769 0.59132 0.23773 1.39675 5.987 66.52
4 (P+O)ES (P+O)進化戦略 0.92256 0.88101 0.40021 2.20379 0.97750 0.87490 0.31945 2.17185 0.67385 0.62985 0.18634 1.49003 5.866 65.17
5 CTA 彗星の尾アルゴリズム(joo) 0.95346 0.86319 0.27770 2.09435 0.99794 0.85740 0.33949 2.19484 0.88769 0.56431 0.10512 1.55712 5.846 64.96
6 TETA 時間進化移動アルゴリズム(joo) 0.91362 0.82349 0.31990 2.05701 0.97096 0.89532 0.29324 2.15952 0.73462 0.68569 0.16021 1.58052 5.797 64.41
7 SDSm 確率的拡散探索M 0.93066 0.85445 0.39476 2.17988 0.99983 0.89244 0.19619 2.08846 0.72333 0.61100 0.10670 1.44103 5.709 63.44
8 BOAm ビリヤード最適化アルゴリズムM 0.95757 0.82599 0.25235 2.03590 1.00000 0.90036 0.30502 2.20538 0.73538 0.52523 0.09563 1.35625 5.598 62.19
9 AAm アーチェリーアルゴリズムM 0.91744 0.70876 0.42160 2.04780 0.92527 0.75802 0.35328 2.03657 0.67385 0.55200 0.23738 1.46323 5.548 61.64
10 ESG 社会集団の進化(joo) 0.99906 0.79654 0.35056 2.14616 1.00000 0.82863 0.13102 1.95965 0.82333 0.55300 0.04725 1.42358 5.529 61.44
11 SIA 等方的焼きなまし(joo) 0.95784 0.84264 0.41465 2.21513 0.98239 0.79586 0.20507 1.98332 0.68667 0.49300 0.09053 1.27020 5.469 60.76
12 EOm 極値最適化M 0.76166 0.77242 0.31747 1.85155 0.99999 0.76751 0.23527 2.00277 0.74769 0.53969 0.14249 1.42987 5.284 58.71
13 BBO 生物地理学に基づく最適化 0.94912 0.69456 0.35031 1.99399 0.93820 0.67365 0.25682 1.86867 0.74615 0.48277 0.17369 1.40261 5.265 58.50
14 ACS 人工協調探索 0.75547 0.74744 0.30407 1.80698 1.00000 0.88861 0.22413 2.11274 0.69077 0.48185 0.13322 1.30583 5.226 58.06
15 DA 弁証法的アルゴリズム 0.86183 0.70033 0.33724 1.89940 0.98163 0.72772 0.28718 1.99653 0.70308 0.45292 0.16367 1.31967 5.216 57.95
16 BHAm ブラックホールアルゴリズムM 0.75236 0.76675 0.34583 1.86493 0.93593 0.80152 0.27177 2.00923 0.65077 0.51646 0.15472 1.32195 5.196 57.73
17 ASO 無政府社会最適化 0.84872 0.74646 0.31465 1.90983 0.96148 0.79150 0.23803 1.99101 0.57077 0.54062 0.16614 1.27752 5.178 57.54
18 RFO ロイヤルフラッシュ最適化(joo) 0.83361 0.73742 0.34629 1.91733 0.89424 0.73824 0.24098 1.87346 0.63154 0.50292 0.16421 1.29867 5.089 56.55
19 AOSm 原子軌道探索M 0.80232 0.70449 0.31021 1.81702 0.85660 0.69451 0.21996 1.77107 0.74615 0.52862 0.14358 1.41835 5.006 55.63
20 TSEA 亀甲進化アルゴリズム(joo) 0.96798 0.64480 0.29672 1.90949 0.99449 0.61981 0.22708 1.84139 0.69077 0.42646 0.13598 1.25322 5.004 55.60
21 BSA バックトラッキング探索アルゴリズム 0.97309 0.54534 0.29098 1.80941 0.99999 0.58543 0.21747 1.80289 0.84769 0.36953 0.12978 1.34700 4.959 55.10
22 DE 差分進化 0.95044 0.61674 0.30308 1.87026 0.95317 0.78896 0.16652 1.90865 0.78667 0.36033 0.02953 1.17653 4.955 55.06
23 SRA レストラン経営達人アルゴリズム(joo) 0.96883 0.63455 0.29217 1.89555 0.94637 0.55506 0.19124 1.69267 0.74923 0.44031 0.12526 1.31480 4.903 54.48
24 CRO 化学反応の最適化 0.94629 0.66112 0.29853 1.90593 0.87906 0.58422 0.21146 1.67473 0.75846 0.42646 0.12686 1.31178 4.892 54.36
25 BIO 血液型遺伝最適化(joo) 0.81568 0.65336 0.30877 1.77781 0.89937 0.65319 0.21760 1.77016 0.67846 0.47631 0.13902 1.29378 4.842 53.80
26 BSA 鳥群アルゴリズム 0.89306 0.64900 0.26250 1.80455 0.92420 0.71121 0.24939 1.88479 0.69385 0.32615 0.10012 1.12012 4.809 53.44
27 DEA イルカのエコーロケーションアルゴリズム 0.75995 0.67572 0.34171 1.77738 0.89582 0.64223 0.23941 1.77746 0.61538 0.44031 0.15115 1.20684 4.762 52.91
28 HS ハーモニー検索 0.86509 0.68782 0.32527 1.87818 0.99999 0.68002 0.09590 1.77592 0.62000 0.42267 0.05458 1.09725 4.751 52.79
29 SSG 苗木の播種と育成 0.77839 0.64925 0.39543 1.82308 0.85973 0.62467 0.17429 1.65869 0.64667 0.44133 0.10598 1.19398 4.676 51.95
30 BCOm 細菌走化性最適化M 0.75953 0.62268 0.31483 1.69704 0.89378 0.61339 0.22542 1.73259 0.65385 0.42092 0.14435 1.21912 4.649 51.65
31 ABO アフリカ水牛の最適化 0.83337 0.62247 0.29964 1.75548 0.92170 0.58618 0.19723 1.70511 0.61000 0.43154 0.13225 1.17378 4.634 51.49
32 (PO)ES (PO)進化戦略 0.79025 0.62647 0.42935 1.84606 0.87616 0.60943 0.19591 1.68151 0.59000 0.37933 0.11322 1.08255 4.610 51.22
33 FBA フラクタルベースのアルゴリズム 0.79000 0.65134 0.28965 1.73099 0.87158 0.56823 0.18877 1.62858 0.61077 0.46062 0.12398 1.19537 4.555 50.61
34 TSm タブーサーチM 0.87795 0.61431 0.29104 1.78330 0.92885 0.51844 0.19054 1.63783 0.61077 0.38215 0.12157 1.11449 4.536 50.40
35 BSO ブレインストーム最適化 0.93736 0.57616 0.29688 1.81041 0.93131 0.55866 0.23537 1.72534 0.55231 0.29077 0.11914 0.96222 4.498 49.98
36 WOAm 鯨最適化アルゴリズムM 0.84521 0.56298 0.26263 1.67081 0.93100 0.52278 0.16365 1.61743 0.66308 0.41138 0.11357 1.18803 4.476 49.74
37 AEFA 人工電界アルゴリズム 0.87700 0.61753 0.25235 1.74688 0.92729 0.72698 0.18064 1.83490 0.66615 0.11631 0.09508 0.87754 4.459 49.55
38 AEO 人工生態系ベースの最適化アルゴリズム 0.91380 0.46713 0.26470 1.64563 0.90223 0.43705 0.21400 1.55327 0.66154 0.30800 0.28563 1.25517 4.454 49.49
39 CAm ラクダアルゴリズムM 0.78684 0.56042 0.35133 1.69859 0.82772 0.56041 0.24336 1.63149 0.64846 0.33092 0.13418 1.11356 4.444 49.37
40 ACOm 蟻コロニー最適化M 0.88190 0.66127 0.30377 1.84693 0.85873 0.58680 0.15051 1.59604 0.59667 0.37333 0.02472 0.99472 4.438 49.31
41 CMAES 共分散行列適応進化戦略 0.76258 0.72089 0.00000 1.48347 0.82056 0.79616 0.00000 1.61672 0.75846 0.49077 0.00000 1.24923 4.349 48.33
42 BFO-GA 細菌採食の最適化:Ga 0.89150 0.55111 0.31529 1.75790 0.96982 0.39612 0.06305 1.42899 0.72667 0.27500 0.03525 1.03692 4.224 46.93
43 SOA シンプル最適化アルゴリズム 0.91520 0.46976 0.27089 1.65585 0.89675 0.37401 0.16984 1.44060 0.69538 0.28031 0.10852 1.08422 4.181 46.45
44 ABHA 人工蜂の巣アルゴリズム 0.84131 0.54227 0.26304 1.64663 0.87858 0.47779 0.17181 1.52818 0.50923 0.33877 0.10397 0.95197 4.127 45.85
45 ACMO 大気雲モデルの最適化 0.90321 0.48546 0.30403 1.69270 0.80268 0.37857 0.19178 1.37303 0.62308 0.24400 0.10795 0.97503 4.041 44.90
A3 人工原子アルゴリズム 0.70744 0.49461 0.27674 1.47879 0.89102 0.43888 0.17553 1.50543 0.53230 0.32707 0.11431 0.97368 3.958 43.98
RW ランダムウォーク 0.48754 0.32159 0.25781 1.06694 0.37554 0.21944 0.15877 0.75375 0.27969 0.14917 0.09847 0.52734 2.348 26.09


まとめ

本記事では、取引戦略の最適化問題を解決するための人工原子アルゴリズム(A3)を、MQL5で実装しました。原著の著者による理論的な説明には不完全な部分がありましたが、実用的に動作する実装を作成することができました。実施したテストでは、A3が計算速度と解の品質のあいだで、実用上十分なバランスを示しました。調整可能なパラメータがわずか2つであるため、特定の問題に合わせてアルゴリズムを設定する作業も大幅に簡単になります。これは、最適化を専門としていないトレーダーにとって重要な点です。 

A3アルゴリズムは、最適化手法の中で一定の役割を担う可能性があります。人工原子アルゴリズムは、比較的単純なメタヒューリスティックであっても、特定の分野に適切に適応させれば、十分に効果的になり得ることを示しています。概念的なシンプルさ、計算効率、そして十分な解の品質を組み合わせたA3は、アルゴリズムトレーダーのツールとして有用な手法と言えます。

アルゴリズムのソースコードとその適用例は、この記事の付録で公開されています。興味のある方は、それらを利用してA3の性能をご自分で評価し、実際にお試しください。

タブ

図1:各テストに対するアルゴリズムの色階調表示

チャート

図2:アルゴリズムテスト結果のヒストグラム(尺度は0〜100で、高いほど良好。100は理論上の最大値。アーカイブには評価表を算出するスクリプトが含まれている)

A3の長所と短所

長所

  1. 高速
  2. パラメータが少ない

短所

  1. 収束精度が低い

最新バージョンのアルゴリズムコードを収録したアーカイブが記事に添付されています。記事の著者は、正統的なアルゴリズム定義の説明の絶対的な正確さについて責任を負いません。探索性能を向上させるために、それらの多くに変更が加えられています。記事に示された結論と判断は、実験結果に基づいています。


記事で使用されているプログラム

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

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

添付されたファイル |
A3.zip (258.97 KB)
最後のコメント | ディスカッションに移動 (7)
Auvoria Prime
Auvoria Prime | 26 6月 2026 において 16:54

私も同じです。最初読んだときはなかなか難しかったです(笑)。アイデア自体は面白そうですが、共有結合やイオン結合のルール、電子の割り当てがはっきりしていないと、アルゴリズムを正しく評価するのは難しいですね。

私にとって役立つのは、例えば2つの原子と数個の変数を使ったシンプルなステップバイステップの例題で、1回の反復処理が解をどのように変化させるかを正確に示してくれることですね。それがなければ、トレーダーが実際に理解したりテストしたりできるものではなく、単なる概念に感じられてしまいます。

Chacha Ian Maroa
Chacha Ian Maroa | 29 6月 2026 において 18:04
なんてとんでもない発想なんだ。次はビッグバン理論に基づく最適化の概念について書いてください
Ryan L Johnson
Ryan L Johnson | 29 6月 2026 において 21:25
Chacha Ian Maroa #:
なんてクレイジーな発想なんだ。次はビッグバン理論の最適化コンセプトについて書いてほしい
その男はロスコスモスにうんざりして、代わりにmql5.comにたどり着いた。
Robson Bruno Teixeira De Sousa
Robson Bruno Teixeira De Sousa | 2 7月 2026 において 00:15

私の評価

公園に隠されたおもちゃを探している100人の子供たちの グループを想像してみてください。

それぞれの子どもは、それぞれ違う場所からスタートします。

アルゴリズムは次のように動作します:

  1. 最初のラウンドで、アルゴリズムはすべての子供たちを公園内のランダムな場所に分散させます。
  2. その後、各ラウンドで:
    • 一部の子どもたちは、おもちゃに最も近い子どもを確認し、その方向へ少し歩きます。
    • 他の子どもたちは、順調に進んでいる子どもたちの中からランダムに選ばれた1人の後を追います
    • 時折、ある子供が、もっと良い道が見つかるかどうか確かめるために、まったく新しい場所を探索することにします
  3. 子供が公園から出ようとしたときはいつでも、アルゴリズムはこう言います:

    「だめ!公園の境界線の中にとどまって。」

  4. このプロセスが何度も繰り返されます。

最終的には、みんながより良い道を見つけている子から学び合うことで、グループ全体が次第におもちゃに近づいていきます

一言で言えば:

このアルゴリズムは、複数の「人」に問題解決を試みさせ、最良の解決策から学び、時にはさらに良い解決策を見つけるために新しい場所を探索させます。


注:コードはまだ中級レベルであり、若干の調整が必要です。


Andrey Dik
Andrey Dik | 14 7月 2026 において 10:43
Chacha Ian Maroa #:
なんてクレイジーな発想なんだ。次回は、ビッグバン理論の最適化という概念について書いてください

;D

https://www.mql5.com/ja/articles/16963

機械学習におけるガウス過程(第2回):MQL5での分類モデルの実装とテスト 機械学習におけるガウス過程(第2回):MQL5での分類モデルの実装とテスト
MQL5におけるガウス過程ライブラリの主要なインターフェースであるIKernel、ILikelihood、IInferenceの実装について見ていきます。また、合成データを使用してその動作を確認し、分類用および回帰用のインジケータを実装します。これらのインジケータでは、新しいバーが追加されるたびにモデルを再学習するオンラインモードでの動作を確認します。
市場シミュレーション:ポジション表示(IV) 市場シミュレーション:ポジション表示(IV)
ここからは、これまで完全に独立していたさまざまなコンポーネントやアプリケーションを、互いに連携させていきます。Chart Trade、マウスインジケータ、そしてエキスパートアドバイザー(EA)はすでに相互に接続されていましたが、これまでは、取引サーバー上で保有されているポジションをチャート上に直接表示する方法がありませんでした。特に、クロスオーダー方式を使用して管理されることの多いポジションを表示できるようになることで、ここからさまざまなアイデアや今後の実装につなげていくことが可能になります。まだこれらのコンポーネントを実際に動かし始めたばかりではありますが、今後の開発に向けた方向性はすでに見えてきています。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
MQL5での量子ニューラルネットワーク(第3回):量子ビットに基づく仮想量子プロセッサ MQL5での量子ニューラルネットワーク(第3回):量子ビットに基づく仮想量子プロセッサ
数学的アナロジーではなく、量子原理に基づく本格的な量子シミュレータを使用した取引システムの構築に焦点を当てています。このシステムでは、3つの仮想量子ビット、量子ゲート、重ね合わせの原理を使用して市場を分析します。また、MQL5で実装されたMetaTrader 5用自動売買EAとして実装されています。最大の成果は、単なるシミュレーションから、金融情報処理における実際の量子原理の利用へ移行したことです。