English Русский Español Português
preview
决斗者算法

决斗者算法

MetaTrader 5 — 交易 |
38 2
Andrey Dik
Andrey Dik

内容

  1. 引言
  2. 算法实现
  3. 测试结果


引言

在算法交易中,每一毫秒都关乎盈亏,因此为交易策略寻找最优参数是一项关键任务。现代交易者正借助人工智能(AI)和进化算法来微调自己的交易系统。

今天我们介绍一种全新的优化方法 —— 决斗者算法,其灵感源自古老的决斗机制。2015年,由Biyanto带领的一组印尼科学家提出了这一算法,作为传统进化算法的替代方案。其目标是通过对优胜个体与落败个体采用差异化处理,减少变异、交叉算子的“盲目性”。本文将详细剖析决斗者算法的数学原理,在MQL5中完成算法实现,并与其他基于种群的优化方法开展对比分析。


算法实现

不妨把交易场想象成一个竞技场,各类策略在此持续博弈。部分策略在特定市场环境中盈利,另一些则会失效。那么,成功交易者的核心特质是什么?既善于从胜负中汲取经验,又能不断调整和完善自己的交易方案。

这正是决斗者算法所蕴含的核心思想。经典遗传算法对所有个体一视同仁,而决斗者算法却对优胜者与落败者区别对待 —— 这正是二者的本质差异。落败者负责学习:分析优胜个体的策略,并吸收其有效部分;优胜者负责创新:在自身核心策略已验证有效的前提下,尝试新方案。冠军承担示范/指导作用,使最优策略得以传承。

示意图

图例1. DAduelist算法的运行过程

通过算法各主要阶段的可视化图展示了决斗者算法的几个关键环节:初始种群 —— 所有决斗个体拥有均等的起始机会;冠军判定 —— 性能最优的决斗个体以金色高亮标记。决斗环节 —— 展示一对一个体对决,并判定胜负;学习与创新 —— 落败者和优胜者两条不同的演化路径;新决斗个体训练 —— 冠军个体将自身特性传递给下一代;淘汰机制 —— 种群中表现最差的个体被移除。

图中还有循环箭头表示算法的迭代特性;同时标注了最终结果、高亮了冠军个体,并附详细图例,说明各图形元素与关键参数的含义。这份可视化图表能帮助读者快速理解算法的核心思想。

将原理梳理清楚后,我们来编写算法的伪代码。

输入:
- popSize:决斗个体的种群规模
- luckCoefficient:决斗运气系数
- learningProbability:落败个体的学习概率
- innovationProbability:优胜个体的创新概率
- championsCount:冠军个体数量

初始化:
1. 校验并修正冠军数量:
   - 如果小于1,则设置为1
   - 如果大于等于种群规模,则设置为种群规模的1/4
2. 创建空数组winners(优胜者)和losers(落败者)
3. 创建大小为championsCount的champions(冠军)数组

主循环(迭代更新):

如果是第一次迭代:
遍历每一个决斗个体i(索引从0至popSize-1):
遍历每一维度坐标j:
在允许参数范围内随机初始化数值
将revision标识设置为true
退出函数

否则(后续迭代):
   1. 种群扩容:
- 将决斗个体数组扩容至popSize + championsCount
      - 初始化新增决斗个体的数据结构

   2. 判定冠军个体:
      - 按适应度值对所有决斗个体排序(冒泡排序)
      - 取前championsCount个个体作为冠军
      
   3. 训练新决斗个体:
遍历每一个冠军个体i:
调用TrainNewDuelist(i, popSize + i):
            遍历每一维度坐标c:
               新个体[c] = 冠军个体[c] + 高斯分布随机数
               施加参数范围约束

   4. 执行决斗:
      清空winners和losers数组
      遍历从championsCount到总个体数的每一个决斗个体i:         随机选取一名对手
         如果对手不等于i:
            判定胜负DetermineWinnerAndLoser(i, opponent):
               A_Luck = 个体A适应度 × (运气系数 + 随机数 × 运气系数)
               B_Luck = 个体B适应度 × (运气系数 + 随机数 × 运气系数)
               如果(A的适应度 + A_Luck)≥(B的适应度 + B_Luck):
                  A为优胜者,B为落败者
               否则:
                  B为优胜者,A为落败者
               将胜负个体分别存入优胜和落败数组

   5. 改进优化:
      a)落败者学习阶段:
         遍历每一组(落败个体,优胜个体)配对:
            执行学习流程LearningProcess (loser, winner):
               遍历每一维度坐标c:
                  如果随机值 < 学习概率:
                    落败个体[c] = 优胜个体[c]

      b)优胜者创新阶段:
         遍历每一个优胜个体:
            执行创新流程InnovationProcess (winner):
               遍历每一维度坐标c:
                  如果随机值 < 创新概率:
                     winner [c] = 区间内随机值

   6. 淘汰筛选:
      - 按适应度降序对全部决斗个体排序
- 仅保留前popSize个个体

更新(标记):
遍历所有决斗个体i:
      如果个体i的适应度 > 全局最优值global_best:
         更新全局最优global_best = 个体i适应度
         保存最优解的坐标

重复执行主循环,直到满足终止条件

这里重点说明的是该算法的核心特性:冠军个体不参与决斗,而是负责训练新个体;运气系数引入随机因素,让对决结果具备不确定性。差异化方法:落败个体负责学习,优胜个体尝试寻找创新解。通过淘汰最差个体,使得种群规模保持不变。现在,我们开始实现DA_duelist算法代码。

我们定义C_AO_DA_duelist类,该类继承自C_AO基类,初始化参数数组params,并为各参数指定名称与默认值。 

  • SetParams() —— 根据params数组中存储的值,修改内部变量(popSize、luckCoefficient等)。 
  • Init() —— 算法的初始化方法。接收参数范围、参数步长数组以及迭代轮数(epochs)。 
  • 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/en/articles/19093";

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

    ArrayResize (params, 5);

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

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

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

  void Moving   ();
  void Revision ();

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

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

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

C_AO_DA_duelist类的Init方法用于完成算法运行前的准备工作。它的核心任务是在算法启动前,初始化初始参数与各类数据结构。

该方法首先调用标准初始化流程,负责处理参数的取值范围与步长。如果本次标准初始化失败,方法返回false,代表无法继续执行。随后方法对冠军个体数量进行修正:如果该数值小于1,则将其设置为1;如果大于或等于种群规模,则将其缩减为总种群规模的四分之一。接下来,清空并重新分配优胜者、落败者和冠军个体的索引数组,保证数组为空,为算法下一阶段做好准备。最终,方法返回true,代表初始化完成。

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

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

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

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

C_AO_DA_duelist类的DetermineWinnerAndLoser方法用于判定种群内两个个体(决斗个体)对决的胜负。执行步骤如下。

  1. “运气值”计算:分别为两个决斗个体(A和B)计算运气值(A_Luck、B_Luck)。“运气值”由决斗个体的“适应度”f、运气系数luckCoefficient以及u.RNDprobab()生成的随机项共同决定。引入随机量用来模拟对局中的运气因素。
  2. 判定优胜者:对比个体的适应度f与其“运气值”之和。总和更大或相等的一方判定为优胜者。 
  3. 保存结果:将优胜者与落败者的索引,分别存入对应的winners和losers数组。这些数组用于后续的种群分析与种群演化。

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

    C_AO_DA_duelist类的LearningProcess方法实现学习流程:在决斗中落败的个体向优胜个体进行“学习”。该流程的目标是,将优胜个体的部分策略传递给落败个体,以此提升落败个体的性能。下面来看一下该方法内部的执行逻辑:

    遍历各维度坐标:通过"for"循环遍历该个体全部的“坐标”。坐标的总数量由变量"coords"决定。

    概率复制:在循环内部调用u.RNDprobab()生成随机数做概率判断。如果随机数小于学习概率learningProbability,则执行下述操作。

    特征复制:落败个体(loserIndex)从优胜个体(winnerIndex)处“复制”第c维坐标的值。也就是将落败个体对应的特征更新为优胜个体的同维度特征值。从本质上讲,这一步实现了优势策略从强者向弱者的传递。

    总而言之,该方法模拟知识或策略从表现更优个体向劣势个体的传递,这正是演化过程的基础。

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

    C_AO_DA_duelist类的InnovationProcess方法,用于为决斗获胜个体实现“创新”(变异)流程。该流程会对优胜个体的策略引入随机改动,用以探索新的参数可能性。

    遍历各维度坐标:通过"for"循环遍历个体全部c坐标,逻辑和LearningProcess方法类似。

    概率变异:如果u.RNDprobab()生成的随机数小于创新概率innovationProbability,则触发变异。换言之,个体按照设定概率(由innovationProbability指定)尝试改变自身策略。

    新值生成与修正:随机数生成函数u.RNDfromCI()为第c个参数生成一个落在区间rangeMin[c]至rangeMax[c]内的随机值。生成的随机值再经由u.SeInDiSp()做调整。该函数负责将数值转换为给定区间(rangeMin[c], rangeMax[c])内、以步长rangeStep[c]离散取值集合中的有效值。

    因此,该方法让优胜个体尝试新策略,有望提升个体自身特性以及对环境的适配能力。这是演化流程中十分重要的一环。

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

    C_AO_DA_duelist类的TrainNewDuelist方法负责创建新的决斗个体(候选解),并基于当前冠军个体对其进行“训练”,完成初始化。这是一种带随机扰动的继承机制,能够为种群引入基因多样性。

    遍历各维度坐标:通过"for"循环遍历新决斗个体(newDuelistIndex)的全部c参数(坐标),逐个完成赋值。

    计算偏差:计算偏差值,用于确定参数的扰动幅度。

    带变异的继承(高斯分布):调用u.GaussDistribution()获取随机偏差,叠加到冠军个体(championIndex)的第c维特征上。该函数生成服从正态分布(高斯分布)的随机数,为继承而来的特征施加随机扰动。传入参数0、rangeMin[c]、rangeMax[c]、8为正态分布配置:均值0、参数上下限,以及控制分布宽度的参数(8)。

    离散网格对齐(SeInDiSp):变异后得到的新决斗个体特征值,通过u.SeInDiSp()函数转换为离散集合内的有效值。该函数保证新个体参数落在允许区间内,并且匹配参数步长rangeStep[c]。

    最终,新决斗个体获得与冠军个体相近的参数特征,但附带随机扰动。这为种群创造多样性,从而能够探索新的策略。 

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

    C_AO_DA_duelist类的Moving方法是决斗个体演化算法的核心迭代步骤。该方法包含(首次运行时的)种群初始化、繁殖、筛选、决斗、(基于决斗结果的)学习训练、创新以及淘汰操作。演化流程的核心逻辑都封装在该方法内部:

    种群初始化(首次运行):如果是第一次运行(revision为 false),则执行种群初始化:对每一个个体i,按照参数允许的取值范围与步长,为其所有特征j随机赋值。初始化完成后,将revision设置为true,避免后续迭代重复初始化。

    繁殖准备:代表种群的数组a扩容,用于存放由冠军个体生成的新决斗个体。初始化新决斗个体(设置初始状态)。 

    判定冠军个体:按适应度f对种群排序,选出性能最优个体。确定冠军集合 ,随后选取排名前championsCount的个体作为冠军,每位冠军通过TrainNewDuelist方法“繁殖”出一个继承自身特征的新决斗个体。

    决斗准备:清空 "winners"和"losers"数组,为存储新一轮决斗结果做准备。 

    执行决斗:除冠军外,其余所有个体(含新繁殖的决斗个体)各自随机挑选一名对手进行对决。通过DetermineWinnerAndLoser判定每一场决斗的胜负。

    决斗个体训练(学习阶段):落败个体向优胜个体学习,通过LearningProcess,让落败者吸收优胜者的优良特征、调整自身参数。 

    优胜个体创新:优胜个体调用InnovationProcess,尝试改变自身策略,探索新的参数空间。

    重新排序:在完成决斗、学习、创新带来的参数变更后,再次依据适应度f对整个种群重新排序,更新个体排名。 

    淘汰劣质个体:将数组a的大小重置为种群规模popSize,剔除最差个体,维持种群总数恒定。

    以上步骤构成一次完整的演化迭代:繁殖后代、筛选优秀个体、执行学习、更新策略,并保持种群规模恒定。Moving方法实现演化算法的基础操作:繁殖、变异、筛选与学习。

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

    C_AO_DA_duelist类的Revision方法用于更新当前种群中搜索得到的最优解信息。该方法的作用,是在演化过程中持续追踪全局最优解。

    遍历种群:通过"for"循环遍历种群内的每一个个体(i从0到popSize)。 

    适应度对比:循环体内,判断当前个体的适应度 f是否优于当前最优适应度fB。

    更新最优解:如果a[i].f优于fB,则更新fB,也就是让fB保存迄今为止找到的最优适应度。同时,将当前个体的各项特征c复制到cB数组中。即cB用于存储当前最优个体对应的特征参数。

    最后,Revision方法会扫描整个当前种群,找出适应度最高的个体。如果该个体优于当前“历史记录”,则更新最优个体的相关信息(包含其适应度以及参数特征)。该方法是追踪算法寻优进度、获取全局最优解所必需的。

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


    测试结果

    总体来看,根据=研究结果,决斗者算法整体表现还可以。

    DA|Duelist Algorithm|100.0|0.01|0.9|0.1|2.0|
    =============================
    5 Hilly's;函数运行次数:10000;结果:0.9278151663330798
    25 Hilly's;函数运行次数:10000;结果:0.5377820196319314
    500 Hilly's;函数运行次数:10000;结果:0.27792394907287765
    =============================
    5 Forest's;函数运行次数:10000;结果:0.8695700230324329
    25 Forest's;函数运行次数:10000;结果:0.47535947112902815
    500 Forest's;函数运行次数:10000;结果:0.18193288697223736
    =============================
    5 Megacity's;函数运行次数:10000;结果:0.6215384615384616
    25 Megacity's;函数运行次数:10000;结果:0.3356923076923076
    500 Megacity's;函数运行次数:10000;结果:0.11715384615384725
    =============================
    总分:4.34477(48.28%)

    算法运行过程的可视化结果显示:无论低维还是高维场景,参数值的分布离散程度都较小,这表明该算法具备良好的搜索能力。

    Hilly

    DA_duelist在Hilly测试函数上

    Forest

    DA_duelist在Forest测试函数上

    Megacity

    DA_duelist在Megacity测试函数上

    根据测试结果,在所有参与对比的优化算法中,决斗者算法位列第42位。

    # AO 描述 Hilly Hilly
    最终
    Forest Forest
    最终
    Megacity (离散) Megacity
    最终
    最终
    结果
    % of
    最大
    10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)
    1ANS跨邻域搜索0.949480.847760.438572.235811.000000.923340.399882.323230.709230.634770.230911.574916.13468.15
    2CLA密码锁算法(joo)0.953450.871070.375902.200420.989420.917090.316422.222940.796920.693850.193031.683806.10767.86
    3AMOm动物迁徙优化M0.903580.843170.462842.209590.990010.924360.465982.380340.567690.591320.237731.396755.98766.52
    4(P+O)ES(P+O) 进化策略0.922560.881010.400212.203790.977500.874900.319452.171850.673850.629850.186341.490035.86665.17
    5CTA彗星尾算法(joo)0.953460.863190.277702.094350.997940.857400.339492.194840.887690.564310.105121.557125.84664.96
    6TETA时间演化旅行算法(joo)0.913620.823490.319902.057010.970960.895320.293242.159520.734620.685690.160211.580525.79764.41
    7SDSm随机扩散搜索 M0.930660.854450.394762.179880.999830.892440.196192.088460.723330.611000.106701.441035.70963.44
    8BOAm台球优化算法M0.957570.825990.252352.035901.000000.900360.305022.205380.735380.525230.095631.356255.59862.19
    9AAm射箭算法M0.917440.708760.421602.047800.925270.758020.353282.036570.673850.552000.237381.463235.54861.64
    10ESG社会群体的进化(joo)0.999060.796540.350562.146161.000000.828630.131021.959650.823330.553000.047251.423585.52961.44
    11SIA模拟各向同性退火(joo)0.957840.842640.414652.215130.982390.795860.205071.983320.686670.493000.090531.270205.46960.76
    12EOm极值优化M0.761660.772420.317471.851550.999990.767510.235272.002770.747690.539690.142491.429875.28458.71
    13BBO生物地理学优化算法0.949120.694560.350311.993990.938200.673650.256821.868670.746150.482770.173691.402615.26558.50
    14ACS人工协同搜索0.755470.747440.304071.806981.000000.888610.224132.112740.690770.481850.133221.305835.22658.06
    15DA辩证算法0.861830.700330.337241.899400.981630.727720.287181.996530.703080.452920.163671.319675.21657.95
    16BHAm黑洞算法M0.752360.766750.345831.864930.935930.801520.271772.009230.650770.516460.154721.321955.19657.73
    17ASO无序社会优化0.848720.746460.314651.909830.961480.791500.238031.991010.570770.540620.166141.277525.17857.54
    18RFO皇家同花顺优化(joo)0.833610.737420.346291.917330.894240.738240.240981.873460.631540.502920.164211.298675.08956.55
    19AOSm原子轨道搜索M0.802320.704490.310211.817020.856600.694510.219961.771070.746150.528620.143581.418355.00655.63
    20TSEA龟壳演化算法(joo)0.967980.644800.296721.909490.994490.619810.227081.841390.690770.426460.135981.253225.00455.60
    21BSA回溯搜索算法0.973090.545340.290981.809410.999990.585430.217471.802890.847690.369530.129781.347004.95955.10
    22DE差分进化0.950440.616740.303081.870260.953170.788960.166521.908650.786670.360330.029531.176534.95555.06
    23SRA成功餐饮经营者算法 (joo)0.968830.634550.292171.895550.946370.555060.191241.692670.749230.440310.125261.314804.90354.48
    24CRO化学反应优化0.946290.661120.298531.905930.879060.584220.211461.674730.758460.426460.126861.311784.89254.36
    25BIO血液遗传优化算法(joo)0.815680.653360.308771.777810.899370.653190.217601.770160.678460.476310.139021.293784.84253.80
    26BSA鸟群算法0.893060.649000.262501.804550.924200.711210.249391.884790.693850.326150.100121.120124.80953.44
    27DEA差分进化算法0.759950.675720.341711.777380.895820.642230.239411.777460.615380.440310.151151.206844.76252.91
    28HS和声搜索0.865090.687820.325271.878180.999990.680020.095901.775920.620000.422670.054581.097254.75152.79
    29SSG树苗播种和生长0.778390.649250.395431.823080.859730.624670.174291.658690.646670.441330.105981.193984.67651.95
    30BCOm细菌趋化性优化算法M0.759530.622680.314831.697040.893780.613390.225421.732590.653850.420920.144351.219124.64951.65
    31ABO非洲水牛优化0.833370.622470.299641.755480.921700.586180.197231.705110.610000.431540.132251.173784.63451.49
    32(PO)ES(PO) 进化策略0.790250.626470.429351.846060.876160.609430.195911.681510.590000.379330.113221.082554.61051.22
    33FBA基于分形的算法0.790000.651340.289651.730990.871580.568230.188771.628580.610770.460620.123981.195374.55550.61
    34TSm禁忌搜索M0.877950.614310.291041.783300.928850.518440.190541.637830.610770.382150.121571.114494.53650.40
    35BSO头脑风暴优化0.937360.576160.296881.810410.931310.558660.235371.725340.552310.290770.119140.962224.49849.98
    36WOAm鲸鱼优化算法M0.845210.562980.262631.670810.931000.522780.163651.617430.663080.411380.113571.188034.47649.74
    37AEFA人工电场算法0.877000.617530.252351.746880.927290.726980.180641.834900.666150.116310.095080.877544.45949.55
    38AEO基于人工生态系统的优化算法0.913800.467130.264701.645630.902230.437050.214001.553270.661540.308000.285631.255174.45449.49
    39CAm骆驼算法M0.786840.560420.351331.698590.827720.560410.243361.631490.648460.330920.134181.113564.44449.37
    40ACOm蚁群优化M0.881900.661270.303771.846930.858730.586800.150511.596040.596670.373330.024720.994724.43849.31
    41CMAES协方差矩阵自适应演化策略0.762580.720890.000001.483470.820560.796160.000001.616720.758460.490770.000001.249234.34948.33
    42DA_duelist决斗者算法0.927820.537780.277921.743520.869570.475360.181931.526860.621530.335690.117151.074374.34548.28
    43BFO-GA细菌觅食优化 - ga0.891500.551110.315291.757900.969820.396120.063051.428990.726670.275000.035251.036924.22446.93
    44SOA简单优化算法0.915200.469760.270891.655850.896750.374010.169841.440600.695380.280310.108521.084224.18146.45
    45ABHA人工蜂巢算法0.841310.542270.263041.646630.878580.477790.171811.528180.509230.338770.103970.951974.12745.85
    RW随机游走0.487540.321590.257811.066940.375540.219440.158770.753750.279690.149170.098470.527342.34826.09


    总结

    决斗者算法在元启发式优化算法中的表现尚可,在参与对比的种群优化算法中位列前45名之内(具体为第42位)。它虽算不上最优的优化算法,但核心优势在于运算速度快。 

    自适应学习策略 —— 在交易系统优化场景中,这意味着表现欠佳的参数组合能快速向成功组合取经;对非平稳金融时间序列而言,这一点尤为宝贵。 

    利用与探索的平衡 —— 优胜个体不会止步于现有成果,而是持续通过创新寻求性能提升。对于需要适应不断变化市场环境的交易策略而言,这一点至关重要。 

    可控的随机元素(运气系数) —— 为市场不确定性提供了自然的建模方式:再优秀的策略,也可能因短期波动而阶段性失手。 

    精英保留机制 —— 确保最优解得以留存并扩散;放到交易场景中,就相当于保住久经考验的成熟策略,同时不断发掘新机会。

    决斗者算法不失为一种可靠的优化方案。它借用格斗竞技的比喻,为优化领域带来一个简洁而有效的核心思想:向强者学习,在优势之上寻求创新,并把经验传给下一代。

    tab

    图例2. 算法对应各组测试下的色彩分级图

    图表

    图例3. 算法测试结果的直方图(评分范围为0到100,越高越好,其中100为理论上的最高可能得分,压缩包中附有用于计算排名表的脚本)

    决斗者算法的优缺点:

    优点:

    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测试

    本文由MetaQuotes Ltd译自俄文
    原文地址: https://www.mql5.com/ru/articles/19093

    附加的文件 |
    DAvduelistk.zip (262.07 KB)
    最近评论 | 前往讨论 (2)
    Александр Бельмецов
    Александр Бельмецов | 15 8月 2025 在 04:00
    MetaQuotes:

    文章《决斗者算法——Duelist Algorithm》 已发布:

    作者:Andrey Dik

    在擂台上厮杀的决斗者。市场并非擂台,而是片片土地。时而晴空万里,时而深陷泥沼。在冰面上,花样滑冰选手会获胜,但在泥沼中……最终,会造就怎样的战士呢?
    lynxntech
    lynxntech | 15 8月 2025 在 04:21
    Александр Бельмецов #:
    在擂台上交锋的决斗者。市场并非擂台,而是一片战场。时而身处泥泞,时而深陷沼泽。在冰面上,花样滑冰选手会获胜;但在沼泽里……最终,会造就出怎样的战士呢?

    大家可以自己上网查查,这可不是本帖作者杜撰的

    交易策略 交易策略
    各种交易策略的分类都是任意的,下面这种分类强调从交易的基本概念上分类。
    MQL5 自优化 EA(第12部分):利用矩阵分解构建线性分类器 MQL5 自优化 EA(第12部分):利用矩阵分解构建线性分类器
    本文探讨矩阵分解在算法交易中的强大作用,重点介绍其在 MQL5 程序里的应用。从回归模型到多目标分类器,我们通过实战示例演示如何借助 MQL5 内置函数轻松集成这类技术。无论你是预测价格方向,还是构建指标行为模型,这份教程都为你使用矩阵方法搭建智能交易系统打下坚实基础。
    新手在交易中的10个基本错误 新手在交易中的10个基本错误
    新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
    从基础到进阶:对象事件(II) 从基础到进阶:对象事件(II)
    本文将介绍由对象触发的最后三类事件的工作原理。理解这些内容会非常有意思,因为文末我们要实现的功能在很多人看来有些疯狂,但它完全可行,并且效果十分惊艳。