English Русский Español Deutsch Português
preview
科学者コミュニティ最適化 (CoSO):実装編

科学者コミュニティ最適化 (CoSO):実装編

MetaTrader 5トレーディング |
23 0
Andrey Dik
Andrey Dik

内容

  1. アルゴリズムの実装
  2. テスト結果
  3. まとめ


アルゴリズムの実装

前編の記事で説明を始めたアルゴリズムの実装について、引き続き説明します。ここでは、専用のテスト関数を用いて実験的にテストした結果を紹介します。

AssignFunds関数は、CoSOアルゴリズムにおいて、利用可能な「資金」を研究者に配分する仕組みを定義しています。これらのリソースは、学習、移動、またはその後のプロセスへの参加に利用できる機会を表しており、その配分方法が解の進化のダイナミクスに影響を与えます。この関数は、配分可能な資金の総額であるavailableFundsという1つのパラメータを受け取ります。処理はいくつかの段階に分けられています。

資金を2つの部分に分割します。まず、outsiderFundsを計算します。これは「アウトサイダー」に割り当てる資金の一部です。この金額は、利用可能な資金の総額availableFundsに対する割合として計算され、その割合を決める係数としてomegaCurrent(アウトサイダーに割り当てる資金の割合を制御するパラメータ)が使用されます。残りの資金はexistingFundsとして、活動中の研究者に割り当てられます。

    既存の研究者への資金配分 。この処理は、「全体レポート(globalReport)」が存在する場合にのみ実行されます。そのサイズが0より大きい場合、totalRankが計算されます。これは、1からreportSizeまでの等差数列に含まれるすべての順位を合計した値です。つまり、資金を比例配分する際に使用される重みの合計です。その後、existingFundsが1単位ずつ配分されます。各資金について、totalRankを乗じた乱数が生成され、それを使って資金を受け取る研究者を選択します。そして、globalReportを順番に走査します。

    globalReport内の各レコードについて、累積値cumSumが計算されます。この値には、(reportSize - i)に等しい「重み」が加算されます。つまり、レポート上位の研究者ほど大きな重みが与えられ、資金を受け取る確率が高くなります。生成された乱数が現在の研究者に割り当てられた区間内に入った場合(つまり、rnd <= cumSumの場合)、その研究者の保有する資金が1増加します。その後、現在の資金の配分処理を終了し、次の資金の配分へ進みます。

    アウトサイダーの生成 。最後に、outsiderFundsを引数としてCreateOutsiders関数が呼び出されます。この関数は、専用に確保された「アウトサイダー」向けの資金を使用して、新しい研究者を生成します。言い換えると、新たな研究経路を開始するための仕組みです。

      このように、AssignFundsは、確立された研究者に報酬を与える一方で、新しいアイデアやエージェントの出現も同時に促進する、助成金配分のような戦略を実装しています。前者はexistingFundsと順位に基づく配分によって実現され、後者はoutsiderFundsを通じて実現されます。

      //————————————————————————————————————————————————————————————————————
      void C_AO_CoSO::AssignFunds (int availableFunds)
      {
        // Funds for outsiders
        int outsiderFunds = (int)(availableFunds * omegaCurrent);
        int existingFunds = availableFunds - outsiderFunds;
      
        int reportSize = ArraySize (globalReport);
      
        // Distribute funds to existing researchers
        if (reportSize > 0)
        {
          int totalRank = reportSize * (reportSize + 1) / 2;
      
          for (int f = 0; f < existingFunds; f++)
          {
            double rnd = u.RNDprobab () * totalRank;
            double cumSum = 0;
      
            for (int i = 0; i < reportSize; i++)
            {
              cumSum += reportSize - i;
              if (rnd <= cumSum)
              {
                researchers [globalReport [i].index].m++;
                break;
              }
            }
          }
        }
      
        // Create outsiders
        CreateOutsiders (outsiderFunds);
      }
      //————————————————————————————————————————————————————————————————————
      

      CreateOutsiders関数は、CoSOモデル内で「アウトサイダー」のために特別に割り当てられたリソースを使用して、新しい「研究者」を生成するプロセスを定義しています。これらの新しい研究者は、システムに導入される多様性や新しいアイデアの候補を表します。この関数は、アウトサイダーの生成に使用する資金の量を表すoutsiderFundsという1つのパラメータを受け取ります。outsiderFundsが0以下の場合、研究者を生成するための資金が存在しないため、関数は直ちに終了します。

      新しい研究者の数を決定します。まず、maxNewが設定されます。これは、現在の時点で生成できるアウトサイダーの最大数です。この値は現在の個体数に応じて決まります。個体数が100を超えている場合、個体数の増加を抑えるため、maxNewは2に設定されます。それ以外の場合は5に設定されます。これは、個体数の増加を制御するための仕組みです。実際に生成する新しい研究者の数は、1からoutsiderFundsとmaxNewの小さい方までの範囲からランダムに決定されます。これにより、新しい研究者の数が利用可能な資金と設定された上限を超えないようにします。その後、各新しい研究者に割り当てる資金の額が計算されます。これは、総額outsiderFundsをnewResearchersで単純に割った値です。

      新しい研究者をループで生成します。 この関数は、newResearchersで指定された回数だけループ処理をおこないます。まず、既存の研究者配列内に「空いている」場所があるかどうかを検索します。空き場所が見つかった場合(idx != -1)、その場所が使用されます。空き場所がないものの、現在の個体数が最大許容サイズ(maxPopSize)未満である場合は、配列の末尾に新しい場所を追加します(idx = actualPopSize)。

      配列を拡張します(必要な場合)。 空き場所がなく、さらにactualPopSizeがすでにmaxPopSize以上になっている場合は、研究者配列のサイズを拡張できるかどうかを確認します。actualPopSizeがすでにmaxPopSizeに達している場合、新しい最大サイズnewMaxSizeがmaxPopSize + 50として計算されます。ただし、500を超えることはありません。これも個体数の増加を制限するための仕組みです。絶対上限である500に達している場合は、現在の反復処理をスキップし、新しいアウトサイダーは生成されません。それ以外の場合は、maxPopSizeがnewMaxSizeに更新されます。これらの確認が完了した後、idxにはactualPopSizeが設定され、最初の新しい空き場所として使用されます。すべての検索・生成処理を行った後も、何らかの理由でidxが-1のままである場合は、現在の研究者の生成をスキップします。

      新しい研究者を初期化します。 idxの位置が正常に見つかった、または作成された場合、researchers[idx].aliveがtrueに設定され、研究者が「活動中」状態になります。また、資金、すなわち「モチベーション」の値にはfundsPerNewが設定され、「強さ」またはその他のパラメータの初期値にはランダムな値が設定されます。

      座標(解空間における位置)を初期化します。 各座標c(coordsは次元数)について、指定された範囲内からランダムな値を生成して位置を初期化します。その後、SeInDiSp関数を使用してサンプリングステップに合わせてresearchers[idx].x[c]を調整します。また、最初は現在の位置と同じ値として設定される「最良」位置も、researchers[idx].x[c]に設定されます。速度または変化ベクトルは、指定されたパラメータに従う正規分布からランダムな値を取得して初期化されます。

      学術誌確率(論文掲載先学術誌)を初期化します。  各学術誌j(journalsNumは学術誌数)について、学術誌jへの論文掲載確率をランダムな値で初期化します。その後、NormalizeProbabilitiesをresearchers[idx].rhoに対して呼び出し、この研究者に設定されたすべてのrhoの合計が1になるように正規化します。

      個体数の更新。 新しい探索者が配列の末尾に追加された場合、actualPopSizeはidx + 1に更新されます。

        このように、CreateOutsidersは、集団に新しい研究者を動的に追加し、解空間内の位置をランダムに初期化するとともに、割り当てられた資金を新しい研究者間で配分します。maxNewとmaxPopSizeによる制限機構は、個体数を制御し、個体数が際限なく増加することを防ぐ役割を果たします。

        //————————————————————————————————————————————————————————————————————
        void C_AO_CoSO::CreateOutsiders (int outsiderFunds)
        {
          if (outsiderFunds <= 0) return;
        
          // Limit the number of new outsiders, especially if the population is already large
          int maxNew = (actualPopSize > 100) ? 2 : 5;
          int newResearchers = (int)(u.RNDfromCI (1, MathMin (outsiderFunds, maxNew)));
          int fundsPerNew = outsiderFunds / newResearchers;
        
          for (int i = 0; i < newResearchers; i++)
          {
            // Find free space
            int idx = -1;
            for (int j = 0; j < actualPopSize; j++)
            {
              if (!researchers [j].alive)
              {
                idx = j;
                break;
              }
            }
        
            if (idx == -1 && actualPopSize < maxPopSize)
            {
              idx = actualPopSize;
            }
        
            if (idx == -1) // No space, expand the array
            {
              if (actualPopSize >= maxPopSize)
              {
                // Limit population growth
                int newMaxSize = MathMin (maxPopSize + 50, 500);
                if (newMaxSize == maxPopSize) continue; // Limit reached, skip creation
        
                maxPopSize = newMaxSize;
                ArrayResize (researchers, maxPopSize);
                for (int j = actualPopSize; j < maxPopSize; j++)
                {
                  researchers [j].Init (coords, journalsNum);
                  researchers [j].alive = false;
                }
                idx = actualPopSize;
              }
            }
        
            if (idx == -1) continue; // Failed to create
        
            researchers [idx].alive = true;
            researchers [idx].m = fundsPerNew;
            researchers [idx].s = u.RNDprobab ();
        
            // Random initialization
            for (int c = 0; c < coords; c++)
            {
              researchers [idx].x [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
              researchers [idx].x [c] = u.SeInDiSp (researchers [idx].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
              researchers [idx].b [c] = researchers [idx].x [c];
              researchers [idx].v [c] = u.GaussDistribution (0.0, -0.01, 0.01, 1);
            }
        
            // Initialize journal probabilities
            for (int j = 0; j < journalsNum; j++)
            {
              researchers [idx].rho [j] = u.RNDprobab ();
            }
            NormalizeProbabilities (researchers [idx].rho);
        
            if (idx >= actualPopSize) actualPopSize = idx + 1;
          }
        }
        //————————————————————————————————————————————————————————————————————
        

        HireResearchers関数は、十分な助成金を保有している既存の研究者が、新しい研究者を「雇用」するプロセスを定義しています。これは、既存の研究者の特徴を考慮しながら新しい要素(解)を生成する仕組みであり、繁殖または多様化の一種と考えることができます。この関数は、新しい研究者を雇用する「監督者」となる研究者のインデックスを1つのパラメータとして受け取ります。対象となる研究者の保有資金が1以下の場合、雇用に十分な資金がないため、関数は直ちに終了します。

        監督者の資金を分離します。 まず、監督者自身が保持する資金の割合を計算します。これは、監督者が保有する総資金にresearchers[idx].sを乗じた値です。次に、雇用に利用できる資金を計算します。これは残りの資金に相当します。その後、監督者の保有資金を更新します。hireFundsが0以下の場合は雇用できないため、関数を終了します。

        雇用する研究者の数を決定します。 まず、新たに雇用できる研究者の最大数を設定します。現在の個体数が100を超えている場合、maxNewは1に設定されます。それ以外の場合は3に設定されます。これは、個体数の増加を制御するための仕組みです。実際に生成する新しい研究者の数は、1からhireFundsとmaxNewの小さい方までの範囲からランダムに決定されます。その後、新しく雇用される各研究者に割り当てる資金を計算します。これは、hireFundsをnewCountで単純に割った値です。

        新しい研究者を生成するループ(採用)。 その後、関数はnewCountの回数だけループ処理を行います。まず、既存の研究者配列内に「空いている」場所を検索します。空き場所とは、アクティブフラグがfalseに設定されているインデックスです。空き場所が見つかった場合は、その場所が使用されます。空き場所がなく、現在の個体数が最大許容サイズ未満である場合は、配列の末尾に新しいスロットが追加されます。 

        上限に達した場合の雇用可能性を確認します。 空き場所がなく、actualPopSizeがすでにmaxPopSize以上である場合、またはactualPopSizeが絶対上限である500以上に達している場合は、現在の探索者の生成をスキップします。つまり、ループの次の反復へ進むか、処理を終了します。newIdxが-1のままである場合は、新しい探索者のための場所を見つけることも作成することもできなかったことを意味するため、現在のループ処理をスキップします。

        新しい研究者を初期化します。 新しい研究者の場所が正常に確保された場合、researchers[newIdx].aliveをtrueに設定し、新しい研究者を活動中状態にします。また、新しい研究者の資金にはfundsPerNewが設定されます。新しい研究者のpropensityパラメータは、ガウス分布からランダムに抽出した値で初期化されます。この分布の平均値にはresearchers[idx].s(監督者のパラメータ)が使用されるため、新しい研究者は監督者と似た特性を持つようになります。なお、sの値は0~1の範囲に制限されます。

        監督者の特性を継承します。新しい研究者が見つけた最良の位置、つまり「知識」は、監督者の最良位置から直接コピーされます。これにより、新しい研究者は監督者の「経験」を継承することができます。

        位置(座標)を初期化します。 各座標c(coordsは次元数)について、新しい研究者の位置は、監督者の位置にガウス分布から取得したランダムな摂動を加えることで初期化されます。つまり、新しい研究者は監督者と「近い」場所に出現しますが、まったく同じ場所になるわけではありません。位置は指定された範囲内に制限されます。その後、researchers[newIdx].x[c]は、SeInDiSp関数を使用してサンプリングステップに合わせて調整されます。また、速度または変化ベクトルも、正規分布からランダムに値を取得して初期化されます。

        学術誌(論文掲載先)の確率を初期化します。 各学術誌j(journalsNumは学術誌数)について、j番目の学術誌への掲載確率を、ガウス分布から取得したランダムな値で初期化します。この分布の平均値には監督者の掲載確率が使用されます。これにより、新しい研究者は監督者の学術誌選択の傾向を継承しながら、一定のランダムな変動も持つことになります。負のrho値は0に切り詰められます。その後、researchers[newIdx].rhoに対してNormalizeProbabilitiesを呼び出し、この研究者に設定されたすべてのrhoの合計が1になるように正規化します。

        個体数の更新。 新しい探索者が配列の末尾に追加された場合(newIdxがactualPopSize以上の場合)、actualPopSizeをnewIdx + 1に更新します。

          このように、HireResearchersによって、成功した研究者は新しい研究者を生成し、自身の特性の一部を継承させながら、一定のランダムな変動を加えることができます。これにより、探索空間における近傍領域の探索と、成功した戦略の拡散が促進される一方、個体数全体の規模も制御されます。

          //————————————————————————————————————————————————————————————————————
          void C_AO_CoSO::HireResearchers (int idx)
          {
            if (researchers [idx].m <= 1) return;
          
            int keepFunds = (int)(researchers [idx].m * researchers [idx].s);
            int hireFunds = researchers [idx].m - keepFunds;
            researchers [idx].m = keepFunds;
          
            if (hireFunds <= 0) return;
          
            // Limit the number of people hired, especially in case of a large population
            int maxNew = (actualPopSize > 100) ? 1 : 3;
            int newCount = (int)(u.RNDfromCI (1, MathMin (hireFunds, maxNew)));
            int fundsPerNew = hireFunds / newCount;
          
            for (int i = 0; i < newCount; i++)
            {
              // Find free space
              int newIdx = -1;
              for (int j = 0; j < actualPopSize; j++)
              {
                if (!researchers [j].alive)
                {
                  newIdx = j;
                  break;
                }
              }
          
              if (newIdx == -1 && actualPopSize < maxPopSize)
              {
                newIdx = actualPopSize;
              }
          
              if (newIdx == -1) // No space
              {
                if (actualPopSize >= maxPopSize || actualPopSize >= 500) continue; // Skip creation when the limit is reached
              }
          
              if (newIdx == -1) continue; // Unable to find space
          
              researchers [newIdx].alive = true;
              researchers [newIdx].m = fundsPerNew;
              researchers [newIdx].s = u.GaussDistribution (researchers [idx].s, 0, 1, 1);
              if (researchers [newIdx].s < 0) researchers [newIdx].s = 0;
              if (researchers [newIdx].s > 1) researchers [newIdx].s = 1;
          
              // Inherit from a supervisor
              ArrayCopy (researchers [newIdx].b, researchers [idx].b, 0, 0, WHOLE_ARRAY);
          
              // Position near a supervisor
              for (int c = 0; c < coords; c++)
              {
                researchers [newIdx].x [c] = researchers [idx].x [c] + u.GaussDistribution (0.0, -0.01, 0.01, 1);
          
                // Boundary control
                if (researchers [newIdx].x [c] < rangeMin [c]) researchers [newIdx].x [c] = rangeMin [c];
                if (researchers [newIdx].x [c] > rangeMax [c]) researchers [newIdx].x [c] = rangeMax [c];
          
                researchers [newIdx].x [c] = u.SeInDiSp (researchers [newIdx].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
                researchers [newIdx].v [c] = u.GaussDistribution (0.0, -0.01, 0.01, 1);
              }
          
              // Perturbed journal probabilities
              for (int j = 0; j < journalsNum; j++)
              {
                researchers [newIdx].rho [j] = u.GaussDistribution (researchers [idx].rho [j], 0, 1, 1);
                if (researchers [newIdx].rho [j] < 0) researchers [newIdx].rho [j] = 0;
              }
              NormalizeProbabilities (researchers [newIdx].rho);
          
              if (newIdx >= actualPopSize) actualPopSize = newIdx + 1;
            }
          }
          //————————————————————————————————————————————————————————————————————
          

          ComputeStdDev関数は、集団内の活動中のすべての研究者について、目的関数の値の標準偏差を計算するために使用されます。標準偏差は、データが平均値の周囲にどの程度分散しているかを示す指標であり、最適化の文脈では、現在の集団における解の多様性を示す可能性があります。

          平均値の計算
          • mean変数を使用してf値の合計を累積し、count変数を使用して活動中の研究者の数をカウントします。
          • この関数は、researchers配列内の研究者を0からactualPopSize - 1まで順番に処理します。
          • ループ内では、researchers[i]が活動中であるかどうかを確認します。計算では活動中の研究者のみを対象とするためです。
          • 研究者が活動中であれば、そのf値がmeanに加算され、countが1増加します。
          • 最初のループが終了した時点で、countが0の場合(活動中の研究者が1人も見つからなかった場合)、関数は0を返します。
          • 最後に、meanをcountで割ることで、すべての活動中の研究者におけるf値の平均値を求めます。
          分散の計算
          • variance変数を0に初期化します。
          • その後、関数は再びresearchers配列内のすべての研究者を順番に処理します。
          • ここでも、研究者が活動中であるかどうかを確認します。
          • 活動中の研究者について、f値と先ほど計算したmeanとの差を求め、その二乗を計算します。この二乗差をvarianceに加算していきます。
          • 2回目のループが終了した後、varianceをcountで割ります。これにより、活動中の研究者のf値における分散が求められます。 
          標準偏差を計算して返します。 最後に、計算したvarianceの平方根を返します。これが標準偏差です。
          //————————————————————————————————————————————————————————————————————
          double C_AO_CoSO::ComputeStdDev ()
          {
            if (actualPopSize == 0) return 0;
          
            double mean = 0;
            int count = 0;
          
            for (int i = 0; i < actualPopSize; i++)
            {
              if (researchers [i].alive)
              {
                mean += researchers [i].f;
                count++;
              }
            }
          
            if (count == 0) return 0;
            mean /= count;
          
            double variance = 0;
            for (int i = 0; i < actualPopSize; i++)
            {
              if (researchers [i].alive)
              {
                variance += MathPow (researchers [i].f - mean, 2);
              }
            }
            variance /= count;
          
            return MathSqrt (variance);
          }
          //————————————————————————————————————————————————————————————————————
          

          UpdateOmega関数は、omegaCurrentパラメータを動的に調整します。omegaCurrentは、アルゴリズムにおけるアウトサイダーの割合、またはアウトサイダーを選択する確率を表します。この関数の目的は、探索者の集団の現在の状態、特に解空間における分散状況に応じて、探索と活用の戦略を適応的に制御することです。

          まず、関数はComputeStdDev()を呼び出して、currentSigmaを計算します。currentSigmaは、活動中の研究者における目的関数値の現在の標準偏差を表します。currentSigmaが大きい場合は解の多様性が高いことを意味し、currentSigmaが小さい場合は研究者の解が収束していることを意味します。

          ケース1:集団の収束(currentSigma < sigma0) 現在の標準偏差があらかじめ設定されたsigma0の閾値よりも小さい場合、研究者が特定の解に向かって収束または集中し始めていることを示します。この場合、アルゴリズムは、すでに見つかっている解をさらに改善する活用段階へ移行します。ただし、局所最適解への早期収束を防ぎ、より良い解を探索し続けるために、アウトサイダーの割合(omegaCurrent)を増加させます。増加量は「(omegaMax - omegaMin) / 2.0 * epsilonPlus」として計算されます。ここで、omegaMaxとomegaMinはomegaCurrentの上限と下限を定義し、epsilonPlusは増加速度を制御する正の係数です。この仕組みの目的は、現在の全体的な傾向から外れる可能性のある要素や、まったく新しいアプローチを表す要素に、より大きな重みを与えることです。

          ケース2:集団の発散または未収束 (currentSigma >= sigma0)。 標準偏差がsigma0閾値以上の場合、集団が依然として広く分散している、または探索の初期段階にあることを意味します。この場合、アウトサイダーの割合(omegaCurrent)を減少させます。減少率は「(omegaMax - omegaMin) / 2.0 * epsilonMinus」として計算されます。ここで、epsilonMinusは減少速度を制御する負の係数です。これは、集団がすでに十分な多様性を持っている状態で、アルゴリズムがランダムな探索に過剰なリソースを費やすことを防ぐためです。その代わりに、より有望な領域へ焦点を移すことができます。

          範囲制限 (omegaCurrent)。 omegaCurrentを変更した後、関数はその値が指定された範囲内に収まっていることを確認します。omegaCurrentがomegaMinを下回った場合はomegaMinに設定され、omegaMaxを上回った場合はomegaMaxに設定されます。これにより、パラメータが合理的な動作範囲を超えることを防ぎます。

          一般的に、UpdateOmega関数は適応型の探索戦略を実装しています。研究者の集団が急速に収束している兆候(標準偏差が小さい状態)を示した場合、アルゴリズムは「アウトサイダー」、つまり現在の構造を変化させる可能性のある要素を選択する確率を高めます。これにより、局所最適解への収束を回避し、より広い解空間を新たに探索することが促進されます。反対に、集団が依然として広く分散している場合は、アウトサイダーへの重点を下げ、より体系的な探索を行ったり、すでに発見されている有望な領域の活用に重点を移したりします。 

          //————————————————————————————————————————————————————————————————————
          void C_AO_CoSO::UpdateOmega ()
          {
            double currentSigma = ComputeStdDev ();
          
            if (currentSigma < sigma0)
            {
              // Increase the proportion of outsiders at convergence
              omegaCurrent += (omegaMax - omegaMin) / 2.0 * epsilonPlus;
            }
            else
            {
              // Reduce the share of outsiders
              omegaCurrent -= (omegaMax - omegaMin) / 2.0 * epsilonMinus;
            }
          
            // Limit the range
            if (omegaCurrent < omegaMin) omegaCurrent = omegaMin;
            if (omegaCurrent > omegaMax) omegaCurrent = omegaMax;
          }
          //————————————————————————————————————————————————————————————————————
          

          NormalizeProbabilities関数は、数値配列「probs」を正規化し、正しい確率分布として扱える状態に変換するために使用されます。つまり、この関数を実行した後は、probs配列内のすべての要素の合計が1になります。 まず、sum変数を0に初期化します。この変数は、probs配列内のすべての要素の合計を累積するために使用されます。その後、配列のすべての要素を順番に処理するループが開始されます。ループ内では、各probs[i]の値がsumに加算されます。 

          計算されたsumが正の値である場合、2回目のループが開始され、再びprobs配列のすべての要素を処理します。このループでは、各要素をsumで割ります。この結果、各probs[i]は全体の合計に対する対応する割合となり、配列内のすべての要素の合計が1になります。これは標準的な正規化処理です。

          一方、sumが0または負の場合は、通常、確率としては想定されない状態ですが、この関数ではそのケースも処理します。これは、元の値を正規化するための合計値が存在しないことを意味します。たとえば、初期値がすべて0だった場合などです。この場合、関数は一様分布に切り替えます。まず、「(1.0 / size)」によってvalが計算されます。これは、配列内の各要素に同じ確率を割り当て、全体の合計が1になるようにすることを意味します。その後、3回目のループが開始され、probs配列のすべての要素に対して、計算されたvalが設定されます。この結果、配列内の各要素は「1 / size」となり、すべての要素を合計すると1になります。

          この関数は、重みや「生の」確率値の集合を、実際の確率分布として使用できる状態にするための汎用的なユーティリティ関数です。ここでいう「実際の確率分布」とは、すべての確率の合計が1になることを意味します。これは、確率に基づく選択手法、ルーレット方式、モンテカルロ法など、さまざまなアルゴリズムにおいて重要です。適切に正規化することで、計算上のエラーを防ぎ、統計的に正しい動作を保証することができます。 

          //————————————————————————————————————————————————————————————————————
          void C_AO_CoSO::NormalizeProbabilities (double &probs [])
          {
            double sum = 0;
            int size = ArraySize (probs);
          
            for (int i = 0; i < size; i++)
            {
              sum += probs [i];
            }
          
            if (sum > 0)
            {
              for (int i = 0; i < size; i++)
              {
                probs [i] /= sum;
              }
            }
            else
            {
              // Even distribution
              double val = 1.0 / size;
              for (int i = 0; i < size; i++)
              {
                probs [i] = val;
              }
            }
          }
          //————————————————————————————————————————————————————————————————————
          

          CompactPopulation関数は、アルゴリズムにおける研究者集団の規模と構成を管理します。主な目的は、最も効率の低い研究者を削除し、必要に応じて集団を扱いやすい規模まで縮小することで、アルゴリズムの効率とパフォーマンスを維持することです。

          活動中の研究者の数を数えます。 まず、aliveCount変数(生存している研究者の数)を0に初期化します。次に、現在の集団に含まれるすべての研究者を、0からactualPopSize - 1まで順番に処理します。各研究者について、その状態(researchers[i].alive)を確認します。探索者が活動中(aliveプロパティがtrue)であれば、aliveCountを1増加させます。

            圧縮/縮小の条件。 活動中の研究者の数をカウントした後、関数は集団の圧縮が必要かどうかを確認します。以下の2つの条件のいずれかを満たした場合に圧縮処理が実行されます。1つ目は、活動中の研究者の数aliveCountが現在の総個体数actualPopSizeの75%未満になった場合です。2つ目は、現在の個体数actualPopSizeが200を超えた場合です。この場合は、大部分の研究者が生存していたとしても、集団が大きくなりすぎたと判断され、縮小が必要になります。

            圧縮の第一段階(「非活動」の研究者を除去)。 圧縮条件のいずれかが満たされると、圧縮処理が開始されます。まず、新しいインデックスnewIdxを0に初期化します。このインデックスは、活動中の研究者をresearchers配列の先頭部分へ移動する際に、次に使用する空き位置を示します。その後、0からactualPopSize - 1まで、すべての研究者を順番に処理します。ループ内で現在の探索者が「活動中」である場合、すでに正しい位置にあるかどうかを確認します。正しい位置にない場合は、配列の前方へ移動する必要があります。

            その研究者をnewIdxの位置へコピーします。通常、このような処理では、元の位置のresearchers[i].alive = falseを設定し、コピー元の研究者を「削除済み」または「非活動」としてマークします。これは後始末のための処理です。その後、newIdxを1増加させ、次の活動中の研究者を配置する位置へ進みます。この処理が完了すると、すべての「生存している」研究者がresearchers配列の先頭部分に集められます。その後、個体数がaliveCountに更新されます。

            圧縮の第2段階(大規模な集団の縮小)。 第1段階で「非活動」研究者を除去し、生存している研究者を前方へ移動させた後、actualPopSizeが依然として150を超えているかどうかを確認します。これは入れ子になった条件であり、集団の圧縮後も研究者が多数残っている場合にのみ実行されます。actualPopSizeが150を超えている場合、「非活動」研究者を除去した後でも集団が大きすぎるため、最大150までさらに縮小する必要があります。そのために、適応度によるソートを実行します。 これにより、最も高い適応度を持つ研究者が配列の先頭に配置されます。

            「最悪」の研究者を削除します。 150番目の要素以降に位置するすべての研究者、つまりソート後に配列の末尾側に残った研究者を非活動としてマークします(researchers [i]. alive = false)。同時に、それらのmパラメータもリセットします。最後に、actualPopSizeを150に設定し、集団を目的とする最大サイズまで縮小します。この関数によって、集団は以下の状態に維持されます。
            1. メモリを消費し、処理速度を低下させる可能性のある未使用の個体が蓄積することを防ぎます。
            2. 集団が制御不能に増加することを防ぎます。これにより、生産性や効率への悪影響、さらには探索の早期停滞につながる可能性を抑えます。
            3. 集団の規模が臨界点に達した場合、成績の悪い個体を排除することで、一定レベルの品質を維持します。
            //————————————————————————————————————————————————————————————————————
            void C_AO_CoSO::CompactPopulation ()
            {
              // Count living researchers
              int aliveCount = 0;
              for (int i = 0; i < actualPopSize; i++)
              {
                if (researchers [i].alive) aliveCount++;
              }
            
              // If there are too many dead, compactify
              if (aliveCount < actualPopSize * 0.75 || actualPopSize > 200)
              {
                int newIdx = 0;
                for (int i = 0; i < actualPopSize; i++)
                {
                  if (researchers [i].alive)
                  {
                    if (i != newIdx)
                    {
                      // Copy the living researcher to a new location
                      researchers [newIdx] = researchers [i];
                      researchers [i].alive = false;
                    }
                    newIdx++;
                  }
                }
                actualPopSize = aliveCount;
            
                // If the population is still too large, limit it
                if (actualPopSize > 150)
                {
                  // Sort by 'fitness' and keep the best
                  for (int i = 0; i < actualPopSize - 1; i++)
                  {
                    for (int j = i + 1; j < actualPopSize; j++)
                    {
                      if (researchers [i].f < researchers [j].f)
                      {
                        S_Researcher temp = researchers [i];
                        researchers [i] = researchers [j];
                        researchers [j] = temp;
                      }
                    }
                  }
            
                  // Kill the worst
                  for (int i = 150; i < actualPopSize; i++)
                  {
                    researchers [i].alive = false;
                    researchers [i].m = 0;
                  }
                  actualPopSize = 150;
                }
              }
            }
            //————————————————————————————————————————————————————————————————————
            

            CoSOアルゴリズムに残されているRevisionメソッドは、まずa配列内のすべての個体を、インデックス0からaSize - 1まで順番に処理するループを開始します。ループ内では、各a[i]個体について、if (a[i].f > fB)という条件を確認します。現在の個体の目的関数値が、現在のグローバルベスト値であるfBより大きい場合、その値が新しい最良値としてfB = a[i].fに更新されます。同時に、この最良個体のインデックスも保存されます。 

            a配列内のすべての個体を処理するループが終了すると、if (bestIND != -1)という条件が確認されます。この条件がtrueになるのは、以前のfBよりも優れた目的関数値を持つ個体が少なくとも1つ見つかった場合です。新しい最良個体が見つかった場合(bestIND != -1)、ArrayCopy関数が呼び出され、見つかった最良個体のcパラメータがグローバルなcB配列へコピーされます。

            このメソッドの主な目的は、その時点までにアルゴリズムが発見したグローバル最良解の現在の状態を維持することです。CoSOのような進化的アルゴリズムでは、「グローバルベスト」を常に監視し、より優れた解が見つかるたびに更新します。この「グローバルベスト」の結果を利用して、その後の探索方向を決定します。つまり、この関数は、アルゴリズムの各反復において「これまでに見つかった最良解を記憶する」処理を実装したものです。

            //————————————————————————————————————————————————————————————————————
            void C_AO_CoSO::Revision ()
            {
              int bestIND = -1;
              int aSize = ArraySize (a);
            
              for (int i = 0; i < aSize; i++)
              {
                if (a [i].f > fB)
                {
                  fB = a [i].f;
                  bestIND = i;
                }
              }
            
              if (bestIND != -1)
              {
                ArrayCopy (cB, a [bestIND].c, 0, 0, WHOLE_ARRAY);
              }
            }
            //————————————————————————————————————————————————————————————————————
            


            テスト結果

            CoSOアルゴリズムは概ね良好に機能しており、まずまずの結果が得られています。もちろん、パラメータをいろいろ試してみることもできます。
            CoSO|Community of Scientist Optimization|10.0|150.0|3.0|10.0|0.7|
            =============================
            5 Hilly's; Func runs:10000; result:0.8047081198587067
            25 Hilly's; Func runs:10000; result:0.5429326559833119
            500 Hilly's; Func runs:10000; result:0.30916988715342353
            =============================
            5 Forest's; Func runs:10000; result:0.7383405771205314
            25 Forest's; Func runs:10000; result:0.38224371519203115
            500 Forest's; Func runs:10000; result:0.20600693936217676
            =============================
            5 Megacity's; Func runs:10000; result:0.553846153846154
            25 Megacity's; Func runs:10000; result:0.2550769230769231
            500 Megacity's; Func runs:10000; result:0.11129230769230862
            =============================
            All score:3.90362 (43.37%)

            これは、アルゴリズムの実装ロジックが多層構造になっていることによるものですが、このように独特なアルゴリズムの可視化を見たのは今回が初めてです。

            Hilly

            Hillyテスト関数のCoSO

            Forest

            Forestテスト関数のCoSO

            Megacity

            Megacityテスト関数のCoSO

            結果に基づき、CoSOアルゴリズムは参考情報としてランキング表に掲載されています。ここで注目していただきたいのは、ランキング表が次第にコンパクトになっており、すでに45%未満の結果は、すでにこの表の範囲外に出つつあるということです。

            # 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
            CoSO 科学者コミュニティ最適化 0.80471 0.54293 0.30917 1.65681 0.73834 0.38224 0.20600 1.32658 0.55384 0.25507 0.11129 0.92020 3.904 43.37
            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


            まとめ

            CoSOアルゴリズムはテスト関数に対して平均的な結果を示しており、最大可能性能の43%を達成しました。もちろん、私はより印象的な結果を期待していました。テスト環境には、標準的で広く知られている関数を含む、より拡張されたテスト関数セットが用意されています。そのため、アルゴリズムの能力をさらに引き出すために、パラメータの選択や使用する関数の選択について、誰でも追加の実験を行うことができます。

            現在の実装における主な欠点は、計算量が大きいことです。ログ機構、動的なリソース配分、適応型の集団管理を組み合わせた多層構造によって、かなり大きな計算負荷が発生します。そのため、このアルゴリズムは他の集団ベースの手法と比較して明らかに処理速度が遅く、高速な解の探索が必要となる問題への適用が制限されます。

            しかし、CoSOの概念的な基盤には大きな興味深さがあります。科学コミュニティをモデル化することで、これまでにない独自のメカニズムが導入されています。動的な集団は問題の複雑さに応じて計算リソースを自動的に適応させ、ジャーナルシステムは最良の解を失うことなく効率的な情報交換を可能にします。また、競争的な資金配分は厳格なルールを設けることなく自然淘汰を生み出し、研究者の採用メカニズムは局所探索と大域探索の両立という問題を解決しようとしています。

            改善の可能性は明確です。CoSOは完成されたソリューションとして捉えるべきではなく、将来性のある研究プラットフォームとして考えるべきです。このアルゴリズムは、人間のコミュニティにおける社会的メカニズムを計算戦略の源泉とする、進化的最適化における新しい方向性を示しています。適切に発展させることで、CoSOは、適応性と変化への耐性が重要であり、計算時間がそれほど重要視されないタスクにおいて、その有用性を発揮できる可能性があります。

            タブ

            図1:テスト間でのアルゴリズムの色分け比較

            チャート

            図2:アルゴリズムテスト結果のヒストグラム(0から100のスケール、高いほど良い)100は理論上の最大値であり、アーカイブには評価表を計算するためのスクリプトがあります。

            CoSOの長所と短所

            長所

            1. アルゴリズムの新しいバリエーションを開発するための優れた基盤となる。

            短所

            1. 外部パラメータが多い。
            2. いくつかの問題に関して停滞する傾向。
            3. 遅い。

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


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

            # 名前 種類 説明
            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_CoSO.mq5
            スクリプト CoSOテストスタンド

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

            添付されたファイル |
            CoSO.zip (257.36 KB)
            初級から中級まで:FileSaveとFileLoad 初級から中級まで:FileSaveとFileLoad
            FileSaveおよびFileLoadのライブラリ関数を使用するいくつかの方法について見ていきます。特定の状況では、いくつかの制限や扱いにくさがあるため、これらの関数は用途が限られていると考える人も多いでしょう。しかし、この2つの関数がどのように動作するのかを正しく理解しておけば、さまざまな場面で多くの手間を省くことができます。また、これらはログファイルを扱うための優れた方法でもあります。
            MQL5での量子ニューラルネットワーク(第2回):ALGLIBのマルコフ行列を用いたバックプロパゲーション学習 MQL5での量子ニューラルネットワーク(第2回):ALGLIBのマルコフ行列を用いたバックプロパゲーション学習
            量子力学の原理と最新の機械学習手法を組み合わせた、アルゴリズム取引向けの革新的な量子ニューラルネットワークアーキテクチャを紹介します。このシステムには、量子的な効果(共鳴、干渉、デコヒーレンス)、異なる時間スケールに対応した多層記憶、ALGLIBライブラリを用いたマルコフ連鎖、そして適応型パラメータ制御が組み込まれています。すべてMQL5で実装されており、組み込みの行列・ベクトル型を使用することで、MetaTrader 5でのでの実装ハードルを下げています。
            MQL5における建値機能の実装(第2回):ATRとRRRに基づく建値機構 MQL5における建値機能の実装(第2回):ATRとRRRに基づく建値機構
            MQL5におけるATRおよびRRRRベースの建値機構の実装を完了し、パラメータを再入力することなく建値モードを簡単に切り替えられるクラスを新たに実装します。各建値方式の有効性を評価するため、複数のバックテストを実施し、アルゴリズム取引の観点からそれぞれのメリットとデメリットを分析します。
            オプションを使わないオプション取引(第2回):実取引への適用 オプションを使わないオプション取引(第2回):実取引への適用
            シンプルなオプション戦略と、それらをMQL5で実装する方法について説明します。まず基本的なEAを開発し、それを徐々に改良して、より複雑なものへ発展させていきます。