preview
Алгоритм дуэлянта — Duelist Algorithm

Алгоритм дуэлянта — Duelist Algorithm

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

Содержание

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


Введение

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

Сегодня рассмотрим новый подход к оптимизации — Duelist Algorithm (Алгоритм Дуэлянта), который черпает вдохновение из древнего искусства поединков. Алгоритм был разработан в 2015 г. группой индонезийских ученых под руководством Biyanto как альтернатива традиционным эволюционным алгоритмам с целью минимизировать "слепой" характер операторов мутации и кроссовера через дифференцированный подход к победителям и проигравшим. В статье мы детально рассмотрим математическую основу Duelist Algorithm, сделаем реализацию на языке MQL5, и проведем сравнительный анализ с другими популяционными методами оптимизации.


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

Представьте торговую площадку как арену, где различные стратегии постоянно соревнуются друг с другом. Одни стратегии побеждают в определенных рыночных условиях, другие терпят поражение. Но что делает успешного трейдера? Способность учиться как на своих победах, так и на поражениях, постоянно адаптируя и совершенствуя свой подход.

Именно эту концепцию воплощает Duelist Algorithm. В отличие от классических генетических алгоритмов, где все особи обрабатываются одинаково, алгоритм дуэлянта признает фундаментальное различие между победителями и проигравшими. Проигравшие учатся — они анализируют стратегии победителей и перенимают их успешные элементы, а победители склонны к инновациям — они экспериментируют с новыми подходами, зная, что их базовая стратегия уже доказала свою эффективность. Чемпионы наставляют — лучшие стратегии передаются дальше.

illustration

Рисунок 1. Иллюстрация работы алгоритма DA_duelist

Визуализация основных этапов алгоритма демонстрирует все ключевые моменты работы Duelist Algorithm: начальная популяция — все дуэлянты стартуют с равными шансами, определение чемпионов — лучшие дуэлянты выделяются золотым цветом. Дуэли — представлен процесс поединка с определением победителя и проигравшего. Обучение и инновации — два разных пути развития для проигравших и победителей. Тренировка новых дуэлянтов, когда чемпионы передают свои навыки новому поколению и элиминация — худшие дуэлянты удаляются из популяции.

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

Теперь, после подробного разбора, напишем псевдокод алгоритма.

ВХОДНЫЕ ПАРАМЕТРЫ:
- popSize: размер популяции дуэлянтов
- luckCoefficient: коэффициент удачи в дуэлях
- learningProbability: вероятность обучения проигравшего
- innovationProbability: вероятность инновации победителя
- championsCount: количество чемпионов

ИНИЦИАЛИЗАЦИЯ:
1. Проверить и скорректировать championsCount:
   - Если < 1, установить = 1
   - Если >= popSize, установить = popSize / 4
2. Создать пустые массивы winners, losers
3. Создать массив champions размером championsCount

ОСНОВНОЙ ЦИКЛ (Moving):

ЕСЛИ первая итерация:
   ДЛЯ каждого дуэлянта i от 0 до popSize:
      ДЛЯ каждой координаты j:
         Инициализировать случайным значением в допустимых границах
   Установить флаг revision = true
   ВЫХОД из функции

ИНАЧЕ (последующие итерации):
   1. РАСШИРЕНИЕ ПОПУЛЯЦИИ:
      - Увеличить массив дуэлянтов до размера (popSize + championsCount)
      - Инициализировать структуры новых дуэлянтов

   2. ОПРЕДЕЛЕНИЕ ЧЕМПИОНОВ:
      - Отсортировать дуэлянтов по убыванию fitness (пузырьковая сортировка)
      - Первые championsCount дуэлянтов становятся чемпионами
      
   3. ТРЕНИРОВКА НОВЫХ ДУЭЛЯНТОВ:
      ДЛЯ каждого чемпиона i:
         Вызвать TrainNewDuelist (i, popSize + i):
            ДЛЯ каждой координаты c:
               новый_дуэлянт [c] = чемпион [c] + GaussDistribution ()
               Применить ограничения диапазона

   4. ПРОВЕДЕНИЕ ДУЭЛЕЙ:
      Очистить массивы winners и losers
      ДЛЯ каждого дуэлянта i от championsCount до totalDuelists:
         Выбрать случайного противника opponent
         ЕСЛИ opponent != i:
            DetermineWinnerAndLoser(i, opponent):
               A_Luck = fitness [A] * (luckCoef + random () * luckCoef)
               B_Luck = fitness [B] * (luckCoef + random () * luckCoef)
               ЕСЛИ (fitness [A] + A_Luck) >= (fitness [B] + B_Luck):
                  A - победитель, B - проигравший
               ИНАЧЕ:
                  B - победитель, A - проигравший
               Добавить в массивы winners и losers

   5. ПРОЦЕСС УЛУЧШЕНИЯ:
      a) Обучение проигравших:
         ДЛЯ каждой пары (проигравший, победитель):
            LearningProcess (loser, winner):
               ДЛЯ каждой координаты c:
                  ЕСЛИ random () < learningProbability:
                     loser [c] = winner [c]

      b) Инновации победителей:
         ДЛЯ каждого победителя:
            InnovationProcess (winner):
               ДЛЯ каждой координаты c:
                  ЕСЛИ random () < innovationProbability:
                     winner [c] = случайное_значение_в_диапазоне

   6. ЭЛИМИНАЦИЯ:
      - Отсортировать всех дуэлянтов по убыванию fitness
      - Оставить только первые popSize дуэлянтов

ОБНОВЛЕНИЕ (Revision):
   ДЛЯ каждого дуэлянта i:
      ЕСЛИ fitness [i] > глобальный_лучший:
         Обновить глобальный_лучший = fitness [i]
         Сохранить координаты лучшего решения

ПОВТОРЯТЬ основной цикл до достижения критерия останова

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

Напишем класс "C_AO_DA_duelist", который является наследником класса "C_AO" и инициализирует массив параметров (params) и присваивает им названия и значения по умолчанию. 

  • SetParams () — изменяет значения внутренних переменных (popSize, luckCoefficient, и т.д.) на основе значений, хранящихся в массиве "params". 
  • Init () — метод инициализации алгоритма. Он принимает массивы для определения диапазонов и шагов для параметров, а также количество эпох. 
  • Moving ()— метод, который содержит основную логику перемещения "дуэлянтов" (поиска оптимальных параметров). 
  • Revision () — отвечает за анализ результатов и внесение корректив в процесс.

Переменные:
  • luckCoefficient — коэффициент удачи;
  • learningProbability — вероятность обучения;
  • innovationProbability — вероятность инновации;
  • championsCount — количество чемпионов;
  • winners [] — массив индексов выигравших "дуэлянтов";
  • losers [] — массив индексов проигравших "дуэлянтов";
  • champions [] — массив индексов "чемпионов".
Приватные методы:
  • DetermineWinnerAndLoser () определяет победителя и проигравшего в "дуэли";
  • LearningProcess () реализует процесс обучения для проигравшего, используя победителя;
  • InnovationProcess () реализует процесс инноваций для победителя;
  • TrainNewDuelist () обучает нового "дуэлянта" на основе чемпиона.
//————————————————————————————————————————————————————————————————————
class C_AO_DA_duelist : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_DA_duelist () { }
  C_AO_DA_duelist ()
  {
    ao_name = "DA";
    ao_desc = "Duelist Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/19093";

    popSize               = 50;    // количество дуэлянтов
    luckCoefficient       = 0.01;  // коэффициент удачи
    learningProbability   = 0.2;   // вероятность обучения для проигравших
    innovationProbability = 0.1;   // вероятность инновации для победителей
    championsCount        = 5;     // количество чемпионов

    ArrayResize (params, 5);

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

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

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

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double luckCoefficient;       // коэффициент удачи
  double learningProbability;   // вероятность обучения
  double innovationProbability; // вероятность инновации
  int    championsCount;        // количество чемпионов

  private: //---------------------------------------------------------
  int    winners [];            // индексы победителей
  int    losers  [];            // индексы проигравших
  int    champions [];          // индексы чемпионов

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

Метод "Init" класса "C_AO_DA_duelist" предназначен для подготовки алгоритма к выполнению. Его основная задача — инициализировать начальные параметры и структуры данных перед началом работы.

Метод начинает работу с вызова стандартной инициализации, которая обрабатывает диапазоны и шаги для параметров. Если эта стандартная инициализация завершилась неудачно, метод возвращает "false", что сигнализирует о невозможности продолжения. Далее метод корректирует число чемпионов: если оно меньше единицы, устанавливает его равным одному; если оно превышает или равно размеру популяции, уменьшает его до четверти от общего размера популяции. Затем происходит очистка и перераспределение массивов, хранящих индексы победителей, проигравших и чемпионов, чтобы они были пустыми и готовы к следующему этапу работы алгоритма. В конце метод возвращает "true", сигнализируя об успешной инициализации.

//————————————————————————————————————————————————————————————————————
//--- Инициализация
bool C_AO_DA_duelist::Init (const double &rangeMinP  [],
                            const double &rangeMaxP  [],
                            const double &rangeStepP [],
                            const int     epochsP = 0)
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

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

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

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

Метод "DetermineWinnerAndLoser" класса "C_AO_DA_duelist" определяет победителя и проигравшего в дуэли между двумя особями (duelists) популяции, рассмотрим порядок действий.

  1. Расчёт "Удачи". Для каждого дуэлянта (A и B) вычисляется значение "Удачи" (A_Luck и B_Luck). "Удача" зависит от базового значения "f" дуэлянта (его "пригодности" или "значимости"), коэффициента "luckCoefficient", и случайного элемента, полученного с помощью "u.RNDprobab ()". Используется некоторая случайность для имитации эффекта удачи.
  2. Определение победителя. Сравнивается сумма пригодности "f"  дуэлянта и его "удачи". Тот, у кого эта сумма больше или равна, считается победителем. 
  3. Сохранение результатов. Индексы победителя и проигравшего добавляются в соответствующие массивы "winners" и "losers". Эти массивы используются для дальнейшего анализа и эволюции популяции.

    //————————————————————————————————————————————————————————————————————
    //--- Определение победителя и проигравшего в дуэли
    void C_AO_DA_duelist::DetermineWinnerAndLoser (int duelistA, int duelistB)
    {
      // Алгоритм из документа
      double A_Luck = a [duelistA].f * (luckCoefficient + u.RNDprobab () * luckCoefficient);
      double B_Luck = a [duelistB].f * (luckCoefficient + u.RNDprobab () * luckCoefficient);
    
      if ((a [duelistA].f + A_Luck) >= (a [duelistB].f + B_Luck))
      {
        ArrayResize (winners, ArraySize (winners) + 1);
        ArrayResize (losers,  ArraySize (losers) + 1);
        winners [ArraySize (winners) - 1] = duelistA;
        losers  [ArraySize (losers) - 1] = duelistB;
      }
      else
      {
        ArrayResize (winners, ArraySize (winners) + 1);
        ArrayResize (losers,  ArraySize (losers) + 1);
        winners [ArraySize (winners) - 1] = duelistB;
        losers  [ArraySize (losers) - 1] = duelistA;
      }
    }
    //————————————————————————————————————————————————————————————————————

    Метод "LearningProcess" класса "C_AO_DA_duelist" реализует процесс обучения, в котором особь, проигравшая в дуэли, "учится" у победителя. Цель этого процесса — улучшить характеристики проигравшего, передавая ему часть стратегии победителя. Посмотрим, что происходит внутри метода:

    Итерация по координатам. Цикл "for" перебирает все "координаты" особи. Количество координат определяется переменной "coords".

    Вероятностное копирование. Внутри цикла, происходит случайная проверка, используя "u.RNDprobab ()". Если случайное число меньше, чем "learningProbability" (вероятность обучения), происходит следующее действие.

    Копирование характеристики. Проигравший (loserIndex) "заимствует" значение координаты "c" у победителя (winnerIndex). Это означает, что характеристика проигравшей особи обновляется, принимая значение соответствующей характеристики победителя. По сути, проигравший копирует часть стратегии победителя.

    В итоге, метод имитирует передачу знаний или стратегии от более успешной особи к менее успешной, что является базой для эволюционного процесса.

    //————————————————————————————————————————————————————————————————————
    //--- Процесс обучения проигравшего у победителя
    void C_AO_DA_duelist::LearningProcess (int loserIndex, int winnerIndex)
    {
      for (int c = 0; c < coords; c++)
      {
        if (u.RNDprobab () < learningProbability)
        {
          // Проигравший копирует часть стратегии победителя
          a [loserIndex].c [c] = a [winnerIndex].c [c];
        }
      }
    }
    //————————————————————————————————————————————————————————————————————

    Метод "InnovationProcess" класса "C_AO_DA_duelist" реализует процесс "инновации" или мутации для особи, победившей в дуэли. Этот процесс вносит случайные изменения в стратегию победителя, исследуя новые возможности.

    Итерация по координатам. Цикл "for" перебирает все координаты "c" особи, аналогично "LearningProcess".

    Вероятностная мутация. Если случайное число, полученное с помощью "u.RNDprobab ()", меньше "innovationProbability", происходит мутация. Другими словами, с заданным шансом (установленным innovationProbability) особь пробует изменить свою стратегию.

    Генерация и коррекция нового значения. Генератор случайных чисел "u.RNDfromCI ()" создает для параметра "c" случайное значение, которое находится в диапазоне от "rangeMin [c]" до "rangeMax [c]". Полученное случайное значение корректируется с помощью "u.SeInDiSp ()". Эта функция отвечает за приведение значения к дискретному набору значений с шагом "rangeStep [c]" внутри заданного диапазона (rangeMin [c], rangeMax [c]).

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

    //————————————————————————————————————————————————————————————————————
    //--- Процесс инновации для победителя
    void C_AO_DA_duelist::InnovationProcess (int winnerIndex)
    {
      for (int c = 0; c < coords; c++)
      {
        if (u.RNDprobab () < innovationProbability)
        {
          // Победитель пробует новую технику (мутация)
          a [winnerIndex].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
          a [winnerIndex].c [c] = u.SeInDiSp (a [winnerIndex].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————

    Метод "TrainNewDuelist" класса "C_AO_DA_duelist" отвечает за создание нового дуэлянта (особи) и его первоначальную настройку путем "обучения" у текущего чемпиона. Это процесс наследования, но с элементами случайности, что позволяет вводить в популяцию генетическое разнообразие.

    Итерация по координатам. Цикл "for" перебирает все характеристики (координаты) "c" нового дуэлянта (newDuelistIndex), настраивая каждую из них.

    Расчет отклонения (deviation). Вычисляется "deviation", определяющее диапазон возможных изменений, относительно разброса допустимых значений.

    Наследование с мутацией (Gauss Distribution). К характеристике "c" чемпиона (championIndex) добавляется случайное отклонение, полученное с использованием функции "u.GaussDistribution ()". Эта функция генерирует случайное число из нормального распределения (распределение Гаусса), что позволяет вводить случайные изменения в унаследованные характеристики. Аргументы "0", "rangeMin [c]", "rangeMax [c]", "8" задают параметры нормального распределения: среднее значение 0, минимальное и максимальное значения, и параметр, контролирующий ширину распределения (8).

    Приведение к дискретному значению (SeInDiSp). Полученное значение характеристики нового дуэлянта, после мутации, приводится к дискретному набору значений с помощью функции "u.SeInDiSp ()". Эта функция обеспечивает, что параметры нового дуэлянта находятся в допустимом диапазоне и соответствуют шагу "rangeStep [c]".

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

    //————————————————————————————————————————————————————————————————————
    //--- Чемпион тренирует нового дуэлянта
    void C_AO_DA_duelist::TrainNewDuelist (int championIndex, int newDuelistIndex)
    {
      for (int c = 0; c < coords; c++)
      {
        // Новый дуэлянт наследует способности чемпиона с небольшими вариациями
        double deviation = (rangeMax [c] - rangeMin [c]) * 0.1;
        a [newDuelistIndex].c [c] = a [championIndex].c [c] + u.GaussDistribution (0, rangeMin [c], rangeMax [c], 8);
        a [newDuelistIndex].c [c] = u.SeInDiSp (a [newDuelistIndex].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
    //————————————————————————————————————————————————————————————————————

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

    Инициализация популяции (первый запуск). Если это первый запуск (revision равно false), происходит инициализация популяции: для каждой особи "i" задаются значения для всех характеристик "j" случайным образом в соответствии с допустимым диапазоном и шагом. После инициализации "revision" устанавливается в "true", чтобы избежать повторной инициализации при последующих запусках.

    Подготовка к размножению. Размер массива "a" (представляющего популяцию) увеличивается, что позволяет разместить новых дуэлянтов, созданных от чемпионов. Новые дуэлянты инициализируются (устанавливаются начальные состояния). 

    Отбор чемпионов. Популяция сортируется по параметру "f" (приспособленности) — для отбора лучших особей. Определяются чемпионы — лучшие "championsCount" особей. Каждый чемпион использует метод "TrainNewDuelist", чтобы создать нового дуэлянта, который наследует черты чемпиона.

    Подготовка к дуэлям. Массивы "winners" и "losers" очищаются, чтобы подготовиться к новым результатам дуэлей. 

    Проведение дуэлей. Каждая особь, кроме чемпионов, сражается с одним случайным противником (тоже, не являющимся чемпионом). Результат каждой дуэли (кто выиграл, кто проиграл) определяется с использованием "DetermineWinnerAndLoser".

    Обучение дуэлянтов (Learning). Происходит обучение проигравших у победителей. "LearningProcess" позволяет проигравшим изменить свои параметры, перенимая лучшие черты от победителей. 

    Инновации победителей (Innovation). Лучшие особи используют "InnovationProcess", чтобы попытаться изменить свою стратегию и исследовать новые возможности.

    Повторная сортировка. Вся популяция сортируется по параметру "f" заново, чтобы обновить порядок особей после изменений, внесенных в процессе дуэлей, обучения и инноваций. 

    Отбраковка худших. Размер массива "a" возвращается к "popSize", удаляя худших особей, чтобы поддерживать постоянный размер популяции.

    Этот цикл представляет собой один шаг эволюции, в котором происходит создание потомства, отбор лучших, обучение, обновление стратегий и поддержание размера популяции. Метод "Moving" выполняет основные эволюционные операции: размножение, мутацию, отбор и обучение.

    //————————————————————————————————————————————————————————————————————
    //--- Основной шаг алгоритма
    void C_AO_DA_duelist::Moving ()
    {
      // Начальная инициализация популяции
      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 totalDuelists = popSize + championsCount;
      ArrayResize (a, totalDuelists);
    
      // Инициализация новых дуэлянтов
      for (int i = popSize; i < totalDuelists; i++)
      {
        a [i].Init (coords);
      }
    
      // Сортировка для определения чемпионов (используем пузырьковую сортировку)
      for (int i = 0; i < popSize - 1; i++)
      {
        for (int j = 0; j < popSize - i - 1; j++)
        {
          if (a [j].f < a [j + 1].f)
          {
            S_AO_Agent temp = a [j];
            a [j] = a [j + 1];
            a [j + 1] = temp;
          }
        }
      }
    
      // Определяем чемпионов
      for (int i = 0; i < championsCount; i++)
      {
        champions [i] = i;
        // Чемпион тренирует нового дуэлянта
        TrainNewDuelist (i, popSize + i);
      }
    
      // Очистка массивов победителей и проигравших
      ArrayResize (winners, 0);
      ArrayResize (losers,  0);
    
      // Проведение дуэлей (исключая чемпионов)
      for (int i = championsCount; i < totalDuelists; i++)
      {
        // Каждый дуэлянт сражается с одним случайным противником
        int opponent = u.RNDintInRange (championsCount, totalDuelists - 1);
        if (opponent != i)
        {
          DetermineWinnerAndLoser (i, opponent);
        }
      }
    
      // Процесс улучшения дуэлянтов
      int minCount = MathMin (ArraySize (winners), ArraySize (losers));
      for (int i = 0; i < minCount; i++)
      {
        // Проигравшие учатся у победителей
        LearningProcess (losers [i], winners [i]);
      }
    
      for (int i = 0; i < ArraySize (winners); i++)
      {
        // Победители инновируют
        InnovationProcess (winners [i]);
      }
    
      // Сортировка всех дуэлянтов
      for (int i = 0; i < totalDuelists - 1; i++)
      {
        for (int j = 0; j < totalDuelists - i - 1; j++)
        {
          if (a [j].f < a [j + 1].f)
          {
            S_AO_Agent temp = a [j];
            a [j] = a [j + 1];
            a [j + 1] = temp;
          }
        }
      }
    
      // Удаляем худших дуэлянтов
      ArrayResize (a, popSize);
    }
    //————————————————————————————————————————————————————————————————————

    Метод "Revision" класса "C_AO_DA_duelist" служит для обновления информации о лучшем решении, найденном в текущей популяции. Он предназначен для отслеживания глобального оптимума в процессе эволюции.

    Итерация по популяции. Цикл "for" проходит по каждой особи в популяции (i от 0 до popSize). 

    Сравнение приспособленности. Внутри цикла проверяется, является ли приспособленность "f" текущей особи лучше, чем текущая лучшая приспособленность "fB".

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

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

    //————————————————————————————————————————————————————————————————————
    //--- Обновление лучшего и худшего решений
    void C_AO_DA_duelist::Revision ()
    {
      // Обновляем глобальное лучшее решения
      for (int i = 0; i < popSize; i++)
      {
        if (a [i].f > fB)
        {
          fB = a [i].f;
          ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————


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

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

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

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

    Hilly

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

    Forest

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

    Megacity

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

    По результатам тестирования алгоритм дуэлянта занимает 42 место в общем рейтинге алгоритмов оптимизации.

    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)
    1ANSacross neighbourhood search0,949480,847760,438572,235811,000000,923340,399882,323230,709230,634770,230911,574916,13468,15
    2CLAcode lock algorithm (joo)0,953450,871070,375902,200420,989420,917090,316422,222940,796920,693850,193031,683806,10767,86
    3AMOmanimal migration ptimization M0,903580,843170,462842,209590,990010,924360,465982,380340,567690,591320,237731,396755,98766,52
    4(P+O)ES(P+O) evolution strategies0,922560,881010,400212,203790,977500,874900,319452,171850,673850,629850,186341,490035,86665,17
    5CTAcomet tail algorithm (joo)0,953460,863190,277702,094350,997940,857400,339492,194840,887690,564310,105121,557125,84664,96
    6TETAtime evolution travel algorithm (joo)0,913620,823490,319902,057010,970960,895320,293242,159520,734620,685690,160211,580525,79764,41
    7SDSmstochastic diffusion search M0,930660,854450,394762,179880,999830,892440,196192,088460,723330,611000,106701,441035,70963,44
    8BOAmbilliards optimization algorithm M0,957570,825990,252352,035901,000000,900360,305022,205380,735380,525230,095631,356255,59862,19
    9AAmarchery algorithm M0,917440,708760,421602,047800,925270,758020,353282,036570,673850,552000,237381,463235,54861,64
    10ESGevolution of social groups (joo)0,999060,796540,350562,146161,000000,828630,131021,959650,823330,553000,047251,423585,52961,44
    11SIAsimulated isotropic annealing (joo)0,957840,842640,414652,215130,982390,795860,205071,983320,686670,493000,090531,270205,46960,76
    12EOmextremal_optimization_M0,761660,772420,317471,851550,999990,767510,235272,002770,747690,539690,142491,429875,28458,71
    13BBObiogeography based optimization0,949120,694560,350311,993990,938200,673650,256821,868670,746150,482770,173691,402615,26558,50
    14ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
    15DAdialectical algorithm0,861830,700330,337241,899400,981630,727720,287181,996530,703080,452920,163671,319675,21657,95
    16BHAmblack hole algorithm M0,752360,766750,345831,864930,935930,801520,271772,009230,650770,516460,154721,321955,19657,73
    17ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
    18RFOroyal flush optimization (joo)0,833610,737420,346291,917330,894240,738240,240981,873460,631540,502920,164211,298675,08956,55
    19AOSmatomic orbital search M0,802320,704490,310211,817020,856600,694510,219961,771070,746150,528620,143581,418355,00655,63
    20TSEAturtle shell evolution algorithm (joo)0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
    21BSAbacktracking_search_algorithm0,973090,545340,290981,809410,999990,585430,217471,802890,847690,369530,129781,347004,95955,10
    22DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
    23SRAsuccessful restaurateur algorithm (joo)0,968830,634550,292171,895550,946370,555060,191241,692670,749230,440310,125261,314804,90354,48
    24CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
    25BIOblood inheritance optimization (joo)0,815680,653360,308771,777810,899370,653190,217601,770160,678460,476310,139021,293784,84253,80
    26BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
    27DEAdolphin_echolocation_algorithm0,759950,675720,341711,777380,895820,642230,239411,777460,615380,440310,151151,206844,76252,91
    28HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
    29SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
    30BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
    31ABOafrican buffalo optimization0,833370,622470,299641,755480,921700,586180,197231,705110,610000,431540,132251,173784,63451,49
    32(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
    33FBAfractal-based Algorithm0,790000,651340,289651,730990,871580,568230,188771,628580,610770,460620,123981,195374,55550,61
    34TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
    35BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
    36WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
    37AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
    38AEOartificial ecosystem-based optimization algorithm0,913800,467130,264701,645630,902230,437050,214001,553270,661540,308000,285631,255174,45449,49
    39CAmcamel algorithm M0,786840,560420,351331,698590,827720,560410,243361,631490,648460,330920,134181,113564,44449,37
    40ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
    41CMAEScovariance_matrix_adaptation_evolution_strategy0,762580,720890,000001,483470,820560,796160,000001,616720,758460,490770,000001,249234,34948,33
    42DA_duelistduelist_algorithm0,927820,537780,277921,743520,869570,475360,181931,526860,621530,335690,117151,074374,34548,28
    43BFO-GAbacterial foraging optimization - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
    44SOAsimple optimization algorithm0,915200,469760,270891,655850,896750,374010,169841,440600,695380,280310,108521,084224,18146,45
    45ABHAartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974,12745,85
    RWrandom walk0,487540,321590,257811,066940,375540,219440,158770,753750,279690,149170,098470,527342,34826,09


    Выводы

    Duelist Algorithm демонстрирует достойные результаты в области метаэвристической оптимизации, занимая место в топ-45 популяционных алгоритмов. Хотя он не претендует на звание абсолютного чемпиона среди оптимизационных методов, его ключевым преимуществом является скорость. 

    Адаптивная стратегия обучения — в контексте оптимизации торговых систем это означает, что неудачные конфигурации параметров быстро перенимают успешные паттерны, что особенно ценно при работе с нестационарными финансовыми рядами. 

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

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

    Иерархическая структура с чемпионами — гарантирует сохранение лучших решений и их распространение, что в трейдинге эквивалентно сохранению проверенных временем стратегий при одновременном поиске новых возможностей.

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

    tab

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

    chart

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

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

    Плюсы:

    1. Быстрый.
    2. Небольшой разброс результатов на тестовых функциях.

    Минусы:

    1. Невысокая точность сходимости.

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


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

    #ИмяТипОписание
    1#C_AO.mqh
    Включаемый файл
    Родительский класс популяционных алгоритмов оптимизации
    2#C_AO_enum.mqh
    Включаемый файл
    Перечисление популяционных алгоритмов оптимизации
    3TestFunctions.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_DA_duelist.mq5
    СкриптИспытательный стенд для DA_duelist
    Прикрепленные файлы |
    DAvduelistk.ZIP (330.07 KB)
    От начального до среднего уровня: Шаблон и Typename (III) От начального до среднего уровня: Шаблон и Typename (III)
    В этой статье мы рассмотрим первую часть темы, которая не так проста для понимания новичками. Чтобы не запутаться еще больше и правильно объяснить данную тему, мы разделим объяснение на этапы. Эту статью мы посвятим первому этапу. Однако, хотя в конце статьи может показаться, что мы зашли в тупик, на самом деле мы сделаем шаг к другой ситуации, которая будет лучше понятна в следующей статье.
    Изучение MQL5 — от новичка до профи (Часть VII): Принципы отладки приложений MQL Изучение MQL5 — от новичка до профи (Часть VII): Принципы отладки приложений MQL
    Исправление ошибок — неотъемлемая часть цикла программирования. В этой статье рассмотрены типовые приемы исправления ошибок (отладки) любого приложения, работающего в среде MetaTrader 5.
    Особенности написания экспертов Особенности написания экспертов
    Написание и тестирование экспертов в торговой системе MetaTrader 4.
    Нейросети в трейдинге: Модель темпоральных запросов (TQNet) Нейросети в трейдинге: Модель темпоральных запросов (TQNet)
    Фреймворк TQNet открывает новые возможности в моделировании и прогнозировании финансовых временных рядов, сочетая модульность, гибкость и высокую производительность. В статье раскрывается возможность реализации сложных механизмом работы с глобальными корреляциями, включая продвинутые методы инициализации параметров.