preview
Оптимизатор Бонобо — Bonobo Optimizer (BO)

Оптимизатор Бонобо — Bonobo Optimizer (BO)

MetaTrader 5Трейдинг |
425 0
Andrey Dik
Andrey Dik

Содержание

  1. Введение
  2. Реализация алгоритма
  3. Результаты тестов
  4. Выводы


Введение

Разберем еще один интересный и многообещающий метод оптимизации, который получил название Bonobo Optimizer. Алгоритм BO был опубликован в 2021 году, а его авторами являются Amit Kumar Das и Dilip Kumar Pratihar. Метод основан на социальном поведении приматов бонобо, которые живут сложной общественной жизнью с несколькими уникальными особенностями. Представьте популяцию, где каждый бонобо —это одно возможное решение для задачи оптимизации. 



Реализация алгоритма

Главная идея алгоритма BO — это динамическая социальная структура "fission-fusion", которая означает, что особи не всегда действуют всей группой сразу, они формируют временные подгруппы для разных активностей. Самый лучший бонобо в популяции называется альфа-бонобо — это текущее лучшее найденное решение, к которому стремятся все остальные.

Алгоритм BO реализует три разные стратегии спаривания, которые имитируют реальное поведение бонобо. Первая стратегия — это рандомизированный выбор, где бонобо создает потомка, двигаясь одновременно к альфа-бонобо (лучшему решению) и к случайному партнеру из подгруппы. Например, если текущий бонобо находится в точке X=5, альфа в точке X=10, а случайный партнер в точке X=7, то потомок может оказаться примерно в точке X=7.5, сделав шаг и к лучшему решению и добавив немного разнообразия от партнера. Коэффициенты scab=1.25 и scsb=1.3 контролируют насколько большие эти шаги, причем они могут быть больше единицы, что позволяет иногда перепрыгивать через лучшее решение для исследования области за ним.

Вторая стратегия — это внегрупповое спаривание "extra-group mating", оно происходит с небольшой вероятностью и имитирует ситуацию когда бонобо ищет партнера из другой группы. В этом случае потомок создается, используя информацию о границах популяции, где находятся самые крайние бонобо по каждой координате. Алгоритм смотрит на позицию альфа-бонобо относительно текущей особи и решает двигаться к верхней границе диапазона или к нижней, используя экспоненциальные коэффициенты "beta", которые создают нелинейные прыжки в пространстве решений. Например, если альфа справа от текущего бонобо, то потомок делает прыжок вправо к максимальной границе, если альфа слева, то прыжок влево к минимальной границе. Это помогает исследовать крайние области пространства поиска.

Третья стратегия — это временное спаривание "consorship mating", где два бонобо формируют временную пару и потомок создается как комбинация их позиций с использованием направленного движения. Здесь важную роль играет флаг "direction", который определяется сравнением фитнеса текущего бонобо и его партнера. Если текущий бонобо лучше партнера, то direction=1 и движение идет от партнера, если хуже, то direction=-1 и движение идет к партнеру. Например, если два бонобо в точках X=5 и X=8, и первый лучше, то потомок создается примерно в точке X=4 или X=6, двигаясь от худшего партнера, а если первый хуже, то потомок будет ближе к X=8, двигаясь к лучшему из двух решений. Коэффициент "exp(-random)" создает затухающие шаги, уменьшающиеся со временем.

Критически важный механизм в алгоритме, это "acceptance criteria" или критерий принятия нового решения. Бонобо не всегда принимает созданного потомка — новое решение принимается только если оно лучше, чем текущее, или с небольшой вероятностью принимается случайно, даже если хуже. Это предотвращает деградацию популяции и гарантирует, что плохие решения не вытесняют хорошие. Например, если у бонобо фитнес 0.8, а потомок имеет фитнес 0.7, то в 97 процентах случаев потомок отвергается, и бонобо остается на своей текущей позиции, и только в 3 процентах случаев принимается для разнообразия популяции. Если потомок лучше (фитнес 0.9), он всегда принимается.

Алгоритм использует адаптивную систему положительных и отрицательных фаз, которая автоматически настраивает параметры в зависимости от прогресса оптимизации. Когда находится новое лучшее решение, начинается положительная фаза "positive phase", где счетчик "ppc" увеличивается, вероятность рандомизированного спаривания растет, становясь более агрессивным (может достичь 1.0), размер подгрупп увеличивается для более широкого поиска, а вероятность внегруппового спаривания возвращается к минимуму — 3 процента. Это период интенсивной эксплуатации найденной перспективной области.

Когда несколько итераций проходит без улучшения, начинается отрицательная фаза "negative phase", где счетчик "npc" растет, вероятность рандомизированного выбора партнера падает (может упасть до 0), размер подгрупп уменьшается для более осторожного поиска, а вероятность внегруппового спаривания увеличивается до 50 процентов. Это период усиленной разведки "exploration", когда алгоритм пытается найти новые перспективные области пространства поиска. Скорость этих изменений контролируется параметром "rcpp", обычно около 0.0035, что означает, что параметры меняются постепенно, а не скачками.

Например, представьте поиск максимума функции на плоскости, где оптимум находится на вершине холма. В начале 30 бонобо разбросаны случайно по всей плоскости. На каждой итерации каждый бонобо формирует подгруппу из 3-5 соседей, выбирает лучшего партнера из подгруппы, создает потомка, используя одну из трех стратегий спаривания, и, если потомок лучше, переходит на его позицию.

Альфа-бонобо постоянно обновляется, когда кто-то находит лучшую точку. Допустим, один бонобо случайно оказался близко к вершине холма, и его фитнес улучшился — он становится альфа, начинается положительная фаза, все остальные бонобо начинают активно двигаться к нему, создавая потомков в направлении альфы, подгруппы становятся больше, охватывая больше популяции, вероятность рандомизированного спаривания растет до 70-80 процентов, и популяция быстро сходится к области вокруг вершины.

Если после 50 итераций улучшений не наблюдается, то начинается отрицательная фаза, рандомизированный поиск партнера падает до 30-40 процентов, а внегрупповое спаривание возрастает до 20-30 процентов, и бонобо начинают делать более дальние прыжки, исследуя другие части ландшафта на случай, если есть другая более высокая вершина и "acceptance criteria" гарантирует, что даже в период активных прыжков бонобо не покидают хорошие найденные позиции без веской причины — они пробуют новые точки, но возвращаются, если новая точка хуже.

Важной деталью является использование нормализованных весов, которые в сумме дают единицу, это создает выпуклую комбинацию позиций и гарантирует, чтобы потомок не улетел слишком далеко от родителей, плюс граничные условия "clipping" жестко обрезают координаты, если они выходят за допустимые пределы. Экспоненциальные коэффициенты "beta" вычисляются по формуле: exp(r^2 + r - 2/r), где "r" — случайное число. Формула специально подобрана, чтобы создавать коэффициенты в диапазоне от 0.1 до 3 с тяжелыми хвостами распределения, позволяющие иногда делать очень большие прыжки для выхода из локальных оптимумов. Очень напоминает поведение цены, когда происходит резкий скачок, и движение выносит нас на новый уровень. Параметр "direction" умноженный на "exp(-random)" создает направленное движение с экспоненциально убывающей силой, что имитирует затухание активности со временем, наблюдаемое у реальных бонобо.

BO

Рисунок 1. Схема работы алгоритма BO

Диаграмма включает: Initialization - начальная популяция и параметры, Fission-Fusion — динамические подгруппы, Three Mating Strategies — все 3 типа спаривания с формулами, Direction Flag Logic — логика флага направления, Acceptance Criteria — критический механизм отбора (красным) и Positive/Negative Phases — адаптивные режимы. Переходим к написанию псевдокода алгоритма BO.

ИНИЦИАЛИЗАЦИЯ

Создаем популяцию из тридцати бонобо со случайными позициями в допустимых 
границах и вычисляем их фитнес. Лучший становится альфа-бонобо. 
Устанавливаем начальные параметры: вероятность случайного спаривания -
половина, внегруппового - три процента, счетчики фаз - в ноль, фактор размера 
подгрупп - на средний уровень.

ГЛАВНЫЙ ЦИКЛ

Вычисляем максимальный размер подгруппы как минимум два, но не больше 
размера популяции × текущий фактор.

ДЛЯ КАЖДОГО БОНОБО:
  Формируем список остальных бонобо. Выбираем случайное количество от двух 
  до максимума во временную подгруппу. Из подгруппы выбираем лучшего по 
  фитнесу как партнера.
  
  Определяем флаг направления: если текущий лучше партнера, то партнер 
  переназначается на случайного из подгруппы и флаг = +1, иначе флаг = -1.
  
  СОЗДАНИЕ ПОТОМКА:
  Генерируем случайное число и сравниваем с вероятностью фазы.
  
  ЕСЛИ меньше вероятности фазы - СЛУЧАЙНОЕ СПАРИВАНИЕ:
    Для каждой координаты: 
    новая позиция = текущая + 1.25 × случайный вес × (альфа - текущая) + 
                    флаг × 1.3 × (1 - случайный вес) × (текущая - партнер)
  
  ИНАЧЕ для каждой координаты генерируем случайное число:
    
    ЕСЛИ меньше вероятности внегруппового - ВНЕГРУППОВОЕ СПАРИВАНИЕ:
      Вычисляем экспоненциальные коэффициенты бета по формулам 
      с квадратами и обратными величинами случайного числа. 
      Проверяем позицию альфы относительно текущей. В зависимости от этого 
      и направленной вероятности, либо прыгаем к верхней границе -
      новая позиция = текущая + бета × (максимум - текущая),
      либо к нижней - новая позиция = текущая - бета × (текущая - минимум). 
      Четыре возможные стратегии создают нелинейные прыжки к краям популяции.
    
    ИНАЧЕ - КОНСОРТНОЕ СПАРИВАНИЕ:
      Если флаг = +1 или случайное меньше направленной вероятности, то 
      новая позиция = текущая + флаг × exp(-случайное число) × (текущая - партнер),
      иначе копируем позицию партнера.
  
  Ограничиваем координаты потомка границами. Вычисляем его фитнес.
  
  КРИТЕРИЙ ПРИНЯТИЯ:
    Если фитнес потомка лучше текущего, ИЛИ случайное число меньше 
    вероятности внегруппового, то заменяем текущего потомком и, если 
    потомок лучше глобального лучшего, обновляем альфу. Иначе текущий 
    бонобо остается без изменений.

АДАПТАЦИЯ ПАРАМЕТРОВ:
  ЕСЛИ глобальное лучшее улучшилось - ПОЗИТИВНАЯ ФАЗА:
    Обнуляем счетчик негативной фазы, увеличиваем позитивной. Вероятность 
    внегруппового возвращается к трем процентам. Вероятность 
    случайного растет к единице. Фактор размера подгрупп 
    увеличивается. Режим агрессивной эксплуатации найденной области.
  
  ИНАЧЕ - НЕГАТИВНАЯ ФАЗА:
    Увеличиваем счетчик негативной фазы, обнуляем позитивной. Вероятность 
    внегруппового растет к половине. Вероятность случайного падает 
    к нулю. Фактор размера подгрупп уменьшается. Режим интенсивной 
    разведки новых областей.

РЕЗУЛЬТАТ
Возвращаем лучшее найденное значение, позицию альфа бонобо и историю 
сходимости.

Переходим к написанию кода алгоритма. Класс, "C_AO_BO" является специфической реализацией алгоритма оптимизации, основанного на поведении бонобо. Он наследуется от базового класса "C_AO", это предполагает наличие общих механизмов управления оптимизационным процессом.

При создании объекта "C_AO_BO" автоматически устанавливаются некоторые начальные настройки, инициализируются основные параметры, такие как размер популяции (popSize), начальная вероятность внегруппового спаривания (xgmProbInit), коэффициенты разделения (scab, scsb), вероятность смены фазы (rcpp) и максимальный коэффициент размера подгруппы (subgroupFactorMax). Эти параметры также сохраняются в структурированном виде внутри объекта для удобства управления.

SetParams() — метод позволяет обновить внутренние параметры объекта "C_AO_BO" на основе значений, хранящихся в его параметрической структуре. Это удобно для настройки алгоритма перед запуском.

Публичные методы (Init, Moving, Revision), объявленные в базовом классе, "Init" отвечает за инициализацию алгоритма, "Moving" — за основную фазу поиска и перемещения особей в пространстве решений, а "Revision" — за форму пересмотра и корректировки текущего решения.

Публичные данные-члены (параметры):

  • xgmProbInit — начальное значение вероятности, с которой выбирается партнер для спаривания из другой группы (внегруппового спаривания);
  • scab — коэффициент, влияющий на расстояние, на которое перемещается "альфа-бонобо" (лучшая особь);
  • scsb — коэффициент, влияющий на расстояние, на которое перемещается "выбранная особь" (другая особь в популяции);
  • rcpp — вероятность, с которой особь может сменить свою "фазу" поведения;
  • subgroupFactorMax — максимальный коэффициент, ограничивающий размер подгрупп, которые могут формироваться внутри общей популяции.
Приватные данные-члены (внутренние переменные):
  • negPhaseCount, posPhaseCount — счетчики, отслеживающие количество "отрицательных" и "положительных" фаз;
  • xgmProb — текущая вероятность внегруппового спаривания, которая может изменяться в ходе работы алгоритма;
  • subgroupFactorInit и subgroupFactor — переменные, связанные с расчетом и управлением размером подгруппы;
  • phaseProb — вероятность перехода между фазами поведения;
  • directProb — вероятность прямого направления движения, влияющая на шаг поиска;
  • prevBestFitness — значение лучшей приспособленности (fitness) с предыдущего шага, используемое для сравнения и принятия решений;
  • prevState — массив, хранящий предыдущие состояния (позиции) особей, он используется для оценки стабильности и принятия решений на основе предыдущих результатов.

Таким образом, "C_AO_BO" представляет собой сложный алгоритм оптимизации, имитирующий социальное поведение бонобо, где решения принимаются на основе вероятностей, коэффициентов и фазовых переходов, а также учитывается предыдущий опыт (предыдущие состояния и приспособленность).

//————————————————————————————————————————————————————————————————————
class C_AO_BO : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_BO () { }
  C_AO_BO ()
  {
    ao_name = "BO";
    ao_desc = "Bonobo Optimizer";
    ao_link = "https://www.mql5.com/ru/articles/20245";

    popSize           = 50;
    xgmProbInit       = 0.03;
    scab              = 1.25;
    scsb              = 1.3;
    rcpp              = 0.0035;
    subgroupFactorMax = 0.05;

    ArrayResize (params, 6);
    params [0].name = "popSize";            params [0].val = popSize;
    params [1].name = "xgmProbInit";        params [1].val = xgmProbInit;
    params [2].name = "scab";               params [2].val = scab;
    params [3].name = "scsb";               params [3].val = scsb;
    params [4].name = "rcpp";               params [4].val = rcpp;
    params [5].name = "subgroupFactorMax";  params [5].val = subgroupFactorMax;
  }

  void SetParams ()
  {
    popSize           = (int)params [0].val;
    xgmProbInit       = params      [1].val;
    scab              = params      [2].val;
    scsb              = params      [3].val;
    rcpp              = params      [4].val;
    subgroupFactorMax = params      [5].val;
  }

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

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double xgmProbInit;       // Начальная вероятность внегруппового спаривания
  double scab;              // Коэффициент разделения для альфа-бонобо
  double scsb;              // Коэффициент разделения для выбранного бонобо
  double rcpp;              // Вероятность изменения фазы
  double subgroupFactorMax; // Максимальное значение временного коэффициента размера подгруппы

  private: //---------------------------------------------------------
  int    negPhaseCount;     // npc - Отрицательное количество фаз
  int    posPhaseCount;     // ppc - Положительное количество фаз
  double xgmProb;           // p_xgm - Вероятность внегруппового спаривания
  double subgroupFactorInit;// tsgs_factor_initial
  double subgroupFactor;    // tsgs_factor
  double phaseProb;         // p_p - Фазовая вероятность
  double directProb;        // p_d - Вероятность направления
  double prevBestFitness;   // pbestcost

  S_AO_Agent prevState [];  // Старые позиции для критериев приемлемости
};
//————————————————————————————————————————————————————————————————————

Метод "Init" отвечает за начальную инициализацию алгоритма оптимизации "C_AO_BO". Он является частью процесса подготовки к выполнению основного цикла оптимизации.

Базовая инициализация. Первым шагом вызывается метод "StandardInit", который является частью базового класса "C_AO". Этот вызов выполняет общие для всех алгоритмов "C_AO" процедуры инициализации, такие как установка диапазонов поиска (rangeMinP, rangeMaxP) и шагов (rangeStepP), а также количества эпох (epochsP). Если эта базовая инициализация завершается неудачно (возвращает "false"), то метод "Init" также прерывает свою работу и возвращает "false", сигнализируя о невозможности продолжения.

Сброс внутренних счетчиков и переменных: далее, метод сбрасывает внутренние переменные, связанные с фазами поведения и вероятностями, к их начальным значениям.

Инициализация состояний агентов. Метод выделяет память для хранения предыдущих состояний агентов (prevState). Размер этого массива определяется "popSize" (размером популяции). Затем для каждого агента в популяции вызывается его собственный метод "Init", передавая ему информацию о координатах (coords). Это означает, что для каждого "агента" (предполагается, что это отдельная особь или решение в популяции) устанавливается его первоначальное состояние.

Если все предыдущие шаги прошли успешно, метод возвращает "true", алгоритм "C_AO_BO" успешно проинициализирован и готов к работе. В целом, метод "Init" выполняет все необходимые подготовительные действия, устанавливая начальные условия и структуру данных, которые будут использоваться на последующих этапах работы алгоритма оптимизации.

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

  //------------------------------------------------------------------
  negPhaseCount      = 0;
  posPhaseCount      = 0;
  xgmProb            = xgmProbInit;
  subgroupFactorInit = 0.5 * subgroupFactorMax;
  subgroupFactor     = subgroupFactorInit;
  phaseProb          = 0.5;
  directProb         = 0.5;
  prevBestFitness    = -DBL_MAX;

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

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

Метод "Moving" является основной активной фазой алгоритма "C_AO_BO", ответственной за генерацию новых решений (потомков) и обновление текущих позиций особей в пространстве поиска. Если флаг "revision" установлен в "false" (что означает, что это первый вызов "Moving" или алгоритм находится в начальной фазе), вся популяция генерируется случайным образом. Для каждого агента (popSize) и каждой координаты (coords) новое значение генерируется в пределах заданных диапазонов (rangeMin, rangeMax) и с учетом шага (rangeStep). После генерации случайных значений, флаг "revision" устанавливается в "true", и метод завершает свою работу. Это гарантирует, что случайная инициализация выполняется только один раз.

    Основной цикл перемещения (при revision равном true). Вычисляется максимальный размер подгруппы (maxSubgroupSize), который зависит от общего размера популяции (popSize) и текущего коэффициента размера подгруппы (subgroupFactor). Минимальный установлен в 2. Метод проходит по каждому агенту (с индексом i) в популяции. 

    Сохранение предыдущего состояния: текущая позиция (a[i].c) и значение приспособленности (a[i].f) агента сохраняются в "prevState" для последующего использования.

    Формирование списка доступных индексов: создается временный массив "availableIndices", содержащий индексы всех агентов, кроме текущего агента "i".

    Выбор случайной подгруппы: определяется фактический размер подгруппы (actualSubgroupSize), который выбирается случайно (от 2 до maxSubgroupSize). Среди доступных индексов случайным образом выбирается (actualSubgroupSize)  уникальных индексов, формирующих (subgroupIndices). Элементы из "availableIndices" удаляются по мере выбора, чтобы избежать повторений.

    Выбор партнера из подгруппы: из выбранной подгруппы "subgroupIndices" ищется агент с наилучшей приспособленностью (наибольшее значение "f").

    Определение направления:если текущий агент "i" имеет худшую приспособленность, чем лучший партнер из подгруппы, то выбирается новый случайный партнер из этой же подгруппы, и направление (direction) устанавливается в 1 (для движения к лучшему). В противном случае (a[i].f лучше, чем у партнера), направление (direction) устанавливается в -1.

    Создание нового решения (offspring):проверяется фазовая вероятность (phaseProb). Если фазовая вероятность срабатывает, то новое решение "offspring" генерируется на основе линейной комбинации текущей позиции агента (a[i].c), лучшей особи в популяции (cB), партнера из подгруппы (a[partnerIdx].c) и случайных весов. Используются коэффициенты (scab и scsb). Если фазовая вероятность не срабатывает, алгоритм может перейти к одному из двух подтипов:

    EXTRA-GROUP MATING: если случайная вероятность "u.RNDprobab()" меньше или равна вероятности внегруппового спаривания (xgmProb), применяется специальный механизм генерации. Он включает в себя экспоненциальные функции "MathExp" и зависит от направления "cB[c]" по сравнению с (a[i].c[c]) и вероятности прямого направления (directProb). Рассчитываются коэффициенты "betaCoef1" и "betaCoef2" для смещения решения.

    CONSORSHIP MATING: в противном случае (если не EXTRA-GROUP MATING), применяется "CONSORSHIP MATING". Новый вариант решения генерируется на основе текущей позиции, партнера из подгруппы и вероятности прямого направления (directProb). Также используется экспоненциальная функция.

    Сгенерированное решение "offspring" проверяется на соответствие диапазонам поиска (rangeMin, rangeMax). Значения, выходящие за пределы, ограничиваются. Затем, с учетом шага (rangeStep), оно "привязывается" к допустимым значениям. Скорректированное решение "offspring" присваивается текущему агенту "a[i].c". В целом, метод "Moving" моделирует сложный процесс взаимодействия особей в популяции, где каждое решение генерируется на основе комбинации случайности, информации о лучших особях, партнерах в подгруппах и фазовых состояниях, что позволяет эффективно исследовать пространство поиска.

    //————————————————————————————————————————————————————————————————————
    void C_AO_BO::Moving ()
    {
      if (!revision)
      {
        for (int i = 0; i < popSize; i++)
        {
          for (int c = 0; c < coords; c++)
          {
            a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
            a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
        revision = true;
        return;
      }
    
      //------------------------------------------------------------------
      int maxSubgroupSize = (int)MathMax (2.0, MathCeil (popSize * subgroupFactor));
    
      //------------------------------------------------------------------
      for (int i = 0; i < popSize; i++)
      {
        // Сохраняем старое состояние
        ArrayCopy (prevState [i].c, a [i].c, 0, 0, coords);
        prevState [i].f = a [i].f;
    
        // Создаем список индексов без текущего агента
        int availableIndices [];
        ArrayResize (availableIndices, popSize - 1);
        int idx = 0;
        for (int k = 0; k < popSize; k++)
        {
          if (k != i) availableIndices [idx++] = k;
        }
    
        //----------------------------------------------------------------
        // Определяем фактический размер подгруппы
        int actualSubgroupSize = 2 + u.RNDminusOne (maxSubgroupSize - 1);
    
        // Выбираем случайную подгруппу
        int subgroupIndices [];
        ArrayResize (subgroupIndices, actualSubgroupSize);
        for (int k = 0; k < actualSubgroupSize; k++)
        {
          int rndIdx = u.RNDminusOne (popSize - 1 - k);
          subgroupIndices [k] = availableIndices [rndIdx];
          // Удаляем выбранный элемент
          for (int m = rndIdx; m < popSize - 2 - k; m++)
          {
            availableIndices [m] = availableIndices [m + 1];
          }
        }
    
        //----------------------------------------------------------------
        // Выбираем лучшего партнера из подгруппы
        int partnerIdx = subgroupIndices [0];
        for (int k = 1; k < actualSubgroupSize; k++)
        {
          if (a [subgroupIndices [k]].f > a [partnerIdx].f)
          {
            partnerIdx = subgroupIndices [k];
          }
        }
    
        // Определяем направление (флаг)
        int direction;
        if (a [i].f > a [partnerIdx].f)
        {
          partnerIdx = subgroupIndices [u.RNDminusOne (actualSubgroupSize)];
          direction = 1;
        }
        else
        {
          direction = -1;
        }
    
        //----------------------------------------------------------------
        // Создание нового решения
        double offspring [];
        ArrayResize (offspring, coords);
    
        if (u.RNDprobab () <= phaseProb)
        {
          //--------------------------------------------------------------
          // PROMISCUOUS или RESTRICTIVE MATING
          //--------------------------------------------------------------
          for (int c = 0; c < coords; c++)
          {
            double rndWeight = u.RNDprobab ();
    
            offspring [c] = a [i].c [c] +
                            scab * rndWeight * (cB [c] - a [i].c [c]) +
                            direction * scsb * (1.0 - rndWeight) * (a [i].c [c] - a [partnerIdx].c [c]);
          }
        }
        else
        {
          //--------------------------------------------------------------
          // CONSORSHIP или EXTRA-GROUP MATING
          //--------------------------------------------------------------
          for (int c = 0; c < coords; c++)
          {
            if (u.RNDprobab () <= xgmProb)
            {
              //----------------------------------------------------------
              // EXTRA-GROUP MATING
              //----------------------------------------------------------
              double rndValue = u.RNDprobab ();
              if (rndValue < 0.01) rndValue = 0.01;
    
              if (cB [c] >= a [i].c [c])
              {
                if (u.RNDprobab () <= directProb)
                {
                  double betaCoef1 = MathExp (rndValue * rndValue + rndValue - 2.0 / rndValue);
                  offspring [c] = a [i].c [c] + betaCoef1 * (rangeMax [c] - a [i].c [c]);
                }
                else
                {
                  double betaCoef2 = MathExp (-rndValue * rndValue + 2.0 * rndValue - 2.0 / rndValue);
                  offspring [c] = a [i].c [c] - betaCoef2 * (a [i].c [c] - rangeMin [c]);
                }
              }
              else
              {
                if (u.RNDprobab () <= directProb)
                {
                  double betaCoef1 = MathExp (rndValue * rndValue + rndValue - 2.0 / rndValue);
                  offspring [c] = a [i].c [c] - betaCoef1 * (a [i].c [c] - rangeMin [c]);
                }
                else
                {
                  double betaCoef2 = MathExp (-rndValue * rndValue + 2.0 * rndValue - 2.0 / rndValue);
                  offspring [c] = a [i].c [c] + betaCoef2 * (rangeMax [c] - a [i].c [c]);
                }
              }
            }
            else
            {
              //----------------------------------------------------------
              // CONSORSHIP MATING
              //----------------------------------------------------------
              if (direction == 1 || u.RNDprobab () <= directProb)
              {
                offspring [c] = a [i].c [c] + direction * MathExp (-u.RNDprobab ()) * (a [i].c [c] - a [partnerIdx].c [c]);
              }
              else
              {
                offspring [c] = a [partnerIdx].c [c];
              }
            }
          }
        }
    
        //----------------------------------------------------------------
        // Ограничение границами
        for (int c = 0; c < coords; c++)
        {
          if (offspring [c] > rangeMax [c]) offspring [c] = rangeMax [c];
          if (offspring [c] < rangeMin [c]) offspring [c] = rangeMin [c];
          offspring [c] = u.SeInDiSp (offspring [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    
          a [i].c [c] = offspring [c];
        }
      }
    }
    //————————————————————————————————————————————————————————————————————

    Метод "Revision" выполняет несколько ключевых функций после того, как генерируется новое поколение решений (потомков) в результате работы метода "Moving". Он отвечает за оценку и принятие новых решений, обновление глобального наилучшего результата и адаптивное изменение параметров алгоритма.

    Критерий принятия решений (Acceptance Criteria):для каждого агента в популяции принимается решение, будет ли он заменен своим новым (сгенерированным) состоянием, или сохранено его предыдущее состояние. Новое состояние принимается в двух случаях:

    • Если новое значение приспособленности (a[i].f) лучше, чем предыдущее (prevState[i].f), или если сработает случайное условие, основанное на вероятности "xgmProb". Это позволяет сохранить некоторую подвижность даже при ухудшении приспособленности.
    • Если "acceptNew" оказывается "false" (то-есть, новое решение хуже и случайное условие не сработало), агент возвращается к своему предыдущему состоянию, копируя как его координаты (a[i].c = prevState[i].c), так и значение приспособленности (a[i].f = prevState[i].f).

      Обновление глобального лучшего:после того, как определены окончательные состояния агентов на текущем шаге (после применения критерия принятия), происходит обновление глобального наилучшего решения. Перебираются все особи, и если приспособленность текущего агента (a[i].f) лучше, чем текущее глобальное лучшее значение (fB), то глобальное лучшее значение (fB) обновляется. Координаты глобального лучшего решения (cB) копируются из координат текущего агента (a[i].c).

      Обновление параметров (Адаптация):этот раздел метода отвечает за адаптацию параметров алгоритма в зависимости от того, улучшается ли найденное лучшее решение.

      Позитивная фаза: если текущее лучшее значение (fB) лучше, чем предыдущее лучшее значение (prevBestFitness), это считается "позитивной фазой". Счетчик отрицательных фаз сбрасывается в ноль, а счетчик положительных фаз увеличивается. Адаптивные параметры настраиваются следующим образом: 

      • prevBestFitness обновляется до текущего "fB", 
      • xgmProb (вероятность внегруппового спаривания) устанавливается обратно на начальное значение "xgmProbInit", 
      • phaseProb (фазовая вероятность) увеличивается, приближаясь к 1.0 (что означает большую вероятность выбора одних стратегий размножения), 
      • directProb (вероятность прямого направления) устанавливается равной "phaseProb", 
      • subgroupFactor (коэффициент размера подгруппы) увеличивается, разрешая выбор больших подгрупп.

      Негативная фаза: если текущее лучшее значение (fB) не лучше, чем предыдущее лучшее значение, это считается "негативной фазой". Счетчик отрицательных фаз "negPhaseCount" увеличивается. Счетчик положительных фаз "posPhaseCount" сбрасывается в ноль. Адаптивные параметры настраиваются следующим образом:

      • xgmProb увеличивается, приближаясь к 0.5 (что увеличивает шанс принятия худших решений),
      • subgroupFactor уменьшается, уменьшая размер подгрупп,
      • phaseProb уменьшается, приближаясь к 0.0 (что означает меньшую вероятность выбора определенных стратегий размножения),
      • directProb устанавливается обратно на 0.5.

        Таким образом, метод "Revision" динамически корректирует поведение алгоритма, стремясь найти баланс между исследованием новых областей поиска (exploration) и эксплуатацией уже найденных хороших решений (exploitation), основываясь на прогрессе в оптимизации.

        //————————————————————————————————————————————————————————————————————
        void C_AO_BO::Revision ()
        {
          //------------------------------------------------------------------
          // ACCEPTANCE CRITERIA
          //------------------------------------------------------------------
          for (int i = 0; i < popSize; i++)
          {
            bool acceptNew = (a [i].f > prevState [i].f) || (u.RNDprobab () <= xgmProb);
        
            if (!acceptNew)
            {
              // Откатываем к предыдущему состоянию
              ArrayCopy (a [i].c, prevState [i].c, 0, 0, coords);
              a [i].f = prevState [i].f;
            }
          }
        
          //------------------------------------------------------------------
          // Обновляем глобальное лучшее
          for (int i = 0; i < popSize; i++)
          {
            if (a [i].f > fB)
            {
              fB = a [i].f;
              ArrayCopy (cB, a [i].c, 0, 0, coords);
            }
          }
        
          //------------------------------------------------------------------
          // Обновление параметров
          //------------------------------------------------------------------
          if (fB > prevBestFitness)
          {
            // POSITIVE PHASE
            negPhaseCount = 0;
            posPhaseCount = posPhaseCount + 1;
        
            double changeParam = MathMin (0.5, posPhaseCount * rcpp);
            prevBestFitness = fB;
            xgmProb         = xgmProbInit;
            phaseProb       = 0.5 + changeParam;
            directProb      = phaseProb;
            subgroupFactor  = MathMin (subgroupFactorMax, subgroupFactorInit + posPhaseCount * rcpp * rcpp);
          }
          else
          {
            // NEGATIVE PHASE
            negPhaseCount = negPhaseCount + 1;
            posPhaseCount = 0;
        
            double changeParam = -MathMin (0.5, negPhaseCount * rcpp);
            xgmProb        = MathMin (0.5, xgmProbInit + negPhaseCount * rcpp * rcpp);
            subgroupFactor = MathMax (0.0, subgroupFactorInit - negPhaseCount * rcpp * rcpp);
            phaseProb      = 0.5 + changeParam;
            directProb     = 0.5;
          }
        }
        //————————————————————————————————————————————————————————————————————


        Результаты тестов

        Результаты тестирования порадовали, хороший и сильный алгоритм.
        BO|Bonobo Optimizer|50.0|0.03|1.25|1.3|0.0035|0.05|
        =============================
        5 Hilly's; Func runs: 10000; result: 0.7756590260834754
        25 Hilly's; Func runs: 10000; result: 0.638053360829739
        500 Hilly's; Func runs: 10000; result: 0.3290817311313
        =============================
        5 Forest's; Func runs: 10000; result: 0.8808853768283266
        25 Forest's; Func runs: 10000; result: 0.7634448901275042
        500 Forest's; Func runs: 10000; result: 0.25572940087496915
        =============================
        5 Megacity's; Func runs: 10000; result: 0.6107692307692308
        25 Megacity's; Func runs: 10000; result: 0.49846153846153846
        500 Megacity's; Func runs: 10000; result: 0.14246153846153967
        =============================
        All score: 4.89455 (54.38%)

        На визуализации заметен большой разброс результатов на задачах малой размерности, высокие показатели для задач средней размерности, тоже присутствует разброс, хорошо происходит поиск на функциях с максимальным количеством координат, недостаточно только итераций, чтобы показать высокую сходимость.

        Hilly

        BO на тестовой функции Hilly

        Forest

        BO на тестовой функции Forest

        Megacity

        BO на тестовой функции Megacity

        Посмотрим, как алгоритм ведет себя на стандартных тестовых функциях. Тоже достаточно хорошо, однако тип задач имеет значение для алгоритма BO, на функции Shaffer заметны некоторые трудности с большой размерностью.

        Ackley

        BO на стандартной тестовой функции Ackley

        Shaffer

        BO на стандартной тестовой функции Shaffer

        По результатам тестирования алгоритм BO занимает 25 место в рейтинговой таблице лучших популяционных методов оптимизации.

        AO Description Hilly Hilly
        Final
        Forest Forest
        Final
        Megacity (discrete) Megacity
        Final
        Final
        Result
        % of
        MAX
        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)
        1 DOAdingom dingo_optimization_algorithm_M 0,47968 0,45367 0,46369 1,39704 0,94145 0,87909 0,91454 2,73508 0,78615 0,86061 0,84805 2,49481 6,627 73,63
        2 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
        3 CLA code lock algorithm (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
        4 AMOm animal migration ptimization 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
        5 (P+O)ES (P+O) evolution strategies 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
        6 CTA comet tail algorithm (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
        7 TETA time evolution travel algorithm (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
        8 SDSm stochastic diffusion search 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
        9 BOAm billiards optimization algorithm 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
        10 AAm archery algorithm 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
        11 ESG evolution of social groups (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
        12 SIA simulated isotropic annealing (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
        13 EOm extremal_optimization_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
        14 BBO biogeography based optimization 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
        15 ACS artificial cooperative search 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
        16 DA dialectical algorithm 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
        17 BHAm black hole algorithm 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
        18 ASO anarchy society optimization 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
        19 RFO royal flush optimization (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
        20 AOSm atomic orbital search 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
        21 TSEA turtle shell evolution algorithm (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
        22 BSA backtracking_search_algorithm 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
        23 DE differential evolution 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
        24 SRA successful restaurateur algorithm (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
        25 BO bonobo_optimizer 0,77565 0,63805 0,32908 1,74278 0,88088 0,76344 0,25573 1,90005 0,61077 0,49846 0,14246 1,25169 4,895 54,38
        26 CRO chemical reaction optimisation 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
        27 BIO blood inheritance optimization (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
        28 DOA dream_optimization_algorithm 0,85556 0,70085 0,37280 1,92921 0,73421 0,48905 0,24147 1,46473 0,77231 0,47354 0,18561 1,43146 4,825 53,62
        29 BSA bird swarm algorithm 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
        30 DEA dolphin_echolocation_algorithm 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
        31 HS harmony search 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
        32 SSG saplings sowing and growing 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
        33 BCOm bacterial chemotaxis optimization 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
        34 ABO african buffalo optimization 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
        35 (PO)ES (PO) evolution strategies 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
        36 FBA fractal-based Algorithm 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
        37 TSm tabu search 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
        38 BSO brain storm optimization 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
        39 WOAm wale optimization algorithm 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
        40 AEFA artificial electric field algorithm 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
        41 AEO artificial ecosystem-based optimization algorithm 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
        42 CAm camel algorithm 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
        43 ACOm ant colony optimization 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
        44 CMAES covariance_matrix_adaptation_evolution_strategy 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
        45 DA_duelist duelist_algorithm 0,92782 0,53778 0,27792 1,74352 0,86957 0,47536 0,18193 1,52686 0,62153 0,33569 0,11715 1,07437 4,345 48,28
        RW random walk 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


        Выводы

        Bonobo Optimizer демонстрирует стабильно хорошую производительность, набирая 54% по результатам тестирования и занимая 25 место из 45 лучших популяционных алгоритмов, что характеризует его как надежный универсальный метод оптимизации.

        Алгоритм показывает свои лучшие результаты на задачах средней размерности, где проявляется эффективность его основных механизмов, таких как fission-fusion. Социальная структура создает временные подгруппы размером от 2 до максимального значения, определяемого адаптивно, три стратегии спаривания обеспечивают баланс между эксплуатацией найденных решений посредством движения к альфа-бонобо и разведкой новых областей через внегрупповое спаривание с прыжками к границам популяции.

        Критически важный механизм acceptance criteria принимает новые решения только если они лучше предыдущих или случайно, с малой вероятностью, что предотвращает деградацию популяции и является абсолютно необходимым условием работоспособности алгоритма.

        Адаптивная система положительных и отрицательных фаз автоматически переключает алгоритм между режимами exploitation когда найдено улучшение, увеличивая вероятность рандомизированного спаривания до единицы и размер подгрупп, и exploration, когда происходит застой, уменьшая рандомизированное спаривание до нуля и увеличивая внегрупповое до 50 процентов, что создает саморегулирующуюся систему не требующую ручной настройки баланса исследования и эксплуатации.

        На малых размерностях наблюдается повышенный разброс результатов, что объясняется избыточностью механизмов алгоритма для простых задач, где популяция из 30 агентов с тремя различными стратегиями спаривания создает слишком много вариативности и случайности, приводя к нестабильной сходимости, в то время как на средних размерностях эта же сложность становится преимуществом, позволяя эффективно исследовать многомерное пространство поиска через комбинацию локального поиска в подгруппах и глобальных прыжков через внегрупповое спаривание.

        Выбор лучшего партнера из временной подгруппы по фитнесу вместо случайного выбора направляет поиск к перспективным областям, использование коэффициентов scab равного 1.25 и scsb равного 1.3 превышающих единицу позволяет агентам перепрыгивать через текущие лучшие решения исследуя области за ними, что полезно для выхода из локальных оптимумов, а направленный флаг, определяемый сравнением фитнесов текущего агента и партнера создает адаптивное поведение, где сильные агенты исследуют, отдаляясь от слабых партнеров, а слабые эксплуатируют, приближаясь к сильным.

        Алгоритм хорошо подходит для задач оптимизации средней сложности, где требуется баланс между надежностью и производительностью без необходимости тонкой настройки параметров, благодаря встроенной адаптации, однако для очень простых задач малой размерности или критичных по производительности применений, где важна максимальная стабильность результатов могут быть предпочтительнее более специализированные алгоритмы с меньшей внутренней вариативностью. 

        tab

        Рисунок 2. Цветовая градация алгоритмов по соответствующим тестам

        chart

        Рисунок 3. Гистограмма результатов тестирования алгоритмов (по шкале от 0 до 100, чем больше, тем лучше, где 100 — максимально возможный теоретический результат, в архиве скрипт для расчета рейтинговой таблицы)

        Плюсы и минусы алгоритма BO:

        Плюсы:

        1. Эффективен на задачах средней и высокой размерности.

        Минусы:

        1. Много внешних параметров.
        2. Застревает на функциях малой размерности. 

        К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.


        Программы, используемые в статье

        # Имя Тип Описание
        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_BO.mq5
        Скрипт Испытательный стенд для BO
        Прикрепленные файлы |
        BO.zip (369.03 KB)
        Моделирование рынка (Часть 09): Сокеты (III) Моделирование рынка (Часть 09): Сокеты (III)
        Сегодняшняя статья является продолжением предыдущей. В ней мы рассмотрим, как будет реализован советник, сосредоточившись в основном на том, как выполняется серверный код. Кода, приведенного в предыдущей статье, недостаточно для того, чтобы всё работало как надо, поэтому необходимо немного углубиться в него. Поэтому нужно прочитать обе статьи, чтобы лучше понять то, что произойдет.
        Индикатор тепловой карты рынка на основе плотности простых чисел Индикатор тепловой карты рынка на основе плотности простых чисел
        Инновационный индикатор на основе теории простых чисел помогает находить сильные уровни разворота, которые не видят другие трейдеры. Тестирование на 10 активах показало: развороты в математически значимых зонах происходят в 1.5-1.8 раза чаще. Пять практических сценариев применения с конкретными правилами для фильтрации ложных пробоев и точного входа в рынок.
        Нейросети в трейдинге: Спайковая архитектура пространственно-временного анализа рынка (Энкодер) Нейросети в трейдинге: Спайковая архитектура пространственно-временного анализа рынка (Энкодер)
        В статье представлена адаптация фреймворка SDformerFlow, обеспечивающая высокую адаптивность за счёт интеграции спайкового внимания с многооконной свёрткой и взвешенным суммированием элементов Query. Архитектура позволяет каждой голове внимания обучать собственные параметры, что повышает точность и чувствительность модели к структуре анализируемых данных.
        Моделирование рынка (Часть 08): Сокеты (II) Моделирование рынка (Часть 08): Сокеты (II)
        Как вам идея создать что-то практичное с помощью сокетов? В сегодняшней статье мы начнем создавать мини-чат. Давайте рассмотрим вместе, как это делается, - это будет очень интересно. Помните, что приведенный здесь код предназначен исключительно для образовательных целей. Не стоит использовать его в коммерческих целях или в готовых приложениях, так как он не обеспечивает безопасности передачи данных и можно увидеть содержимое, передаваемое по сокету.