Duelist Algorithm
Contents
Introduction
In algorithmic trading, every millisecond means profit or loss, so finding the optimal parameters for a trading strategy becomes a critical task. Modern traders are turning to artificial intelligence and evolutionary algorithms to fine-tune their trading systems.
Today we will look at a new optimization approach — the Duelist Algorithm — that draws inspiration from the ancient practice of dueling. The algorithm was developed in 2015 by a group of Indonesian scientists led by Biyanto as an alternative to traditional evolutionary algorithms with the aim of minimizing the "blind" nature of mutation and crossover operators through a differentiated approach to winners and losers. In this article, we will examine the mathematical basis of the Duelist Algorithm in detail, implement it in MQL5, and conduct a comparative analysis with other population-based optimization methods.
Algorithm implementation
Think of the trading floor as an arena where different strategies constantly compete with each other. Some strategies win in certain market conditions, while others fail. But what makes a successful trader? The ability to learn from both one's victories and defeats, constantly adapting and refining one's approach.
This is precisely the concept the Duelist Algorithm embodies. Unlike classical genetic algorithms, where all individuals are treated equally, the duelist algorithm recognizes a fundamental difference between winners and losers. Losers learn — they analyze the strategies of winners and adopt their successful elements, while winners innovate — they experiment with new approaches, knowing that their core strategy has already proven its efficiency. Champions mentor – the best strategies are passed on.

Figure 1. DA_duelist algorithm in action
The visualization of the main stages of the algorithm demonstrates all the key aspects of the Duelist Algorithm: the initial population - all duelists start with equal chances; champion determination - the best duelists are highlighted in gold. Duels - the process of a duel with the determination of a winner and a loser is presented. Learning and innovation - two different development paths for losers and winners. Training new duelists, where champions pass on their skills to the next generation, and elimination - the worst duelists are removed from the population.
The illustration also includes a loop arrow showing the iterative nature of the algorithm, the final result, highlighting the champion, and a detailed legend explaining all the elements and key parameters. The visualization helps the reader quickly grasp the core idea of the algorithm.
Now, after a detailed analysis, let's write the algorithm pseudocode.
INPUTS:
- popSize: size of the duelist population
- luckCoefficient: luck coefficient in duels
- learningProbability: probability of the loser learning
- innovationProbability: probability of a winner innovation
- championsCount: number of champions
INITIALIZATION:
1. Check and adjust championsCount:
- If < 1, set = 1
- If >= popSize, set = popSize / 4
2. Create empty 'winners' and 'losers' arrays
3. Create an array named 'champions' of size championsCount
MAIN CYCLE (Moving):
IF first iteration:
FOR each duelist i from 0 to popSize:
FOR each coordinate j:
Initialize with a random value within allowed bounds
Set the 'revision' flag = true
EXIT from the function
ELSE (subsequent iterations):
1. POPULATION EXPANSION:
- Increase the duelists array to the size (popSize + championsCount)
- Initialize the structures of new duelists
2. DETERMINING THE CHAMPIONS:
- Sort duelists by fitness level (bubble sort)
- The first championsCount of duelists become champions
3. TRAINING NEW DUELISTS:
FOR each champion i:
Call TrainNewDuelist (i, popSize + i):
FOR each coordinate c:
new_duelist [c] = champion [c] + GaussDistribution ()
Apply range restrictions
4. CONDUCTING DUELS:
Clear the 'winners' and 'losers' arrays
FOR each duelist i from championsCount to totalDuelists:
Select a random 'opponent'
IF opponent != i:
DetermineWinnerAndLoser(i, opponent):
A_Luck = fitness [A] * (luckCoef + random () * luckCoef)
B_Luck = fitness [B] * (luckCoef + random () * luckCoef)
IF (fitness [A] + A_Luck) >= (fitness [B] + B_Luck):
A - winner, B - loser
OTHERWISE:
B - winner, A - loser
Add 'winners' and 'losers' to the arrays
5. IMPROVEMENT:
a) Learning phase for losers:
FOR each pair (loser, winner):
LearningProcess (loser, winner):
FOR each coordinate c:
IF random () < learningProbability:
loser [c] = winner [c]
b) Winner innovations:
FOR each winner:
InnovationProcess (winner):
FOR each coordinate c:
IF random () < innovationProbability:
winner [c] = random_value_in_range
6. ELIMINATION:
- Sort all duelists in descending order by 'fitness'
- Leave only the first popSize duelists
UPDATE (Revision):
FOR each duelist i:
IF fitness [i] > global_best:
Update global_best = fitness [i]
Save the coordinates of the best solution
REPEAT the main loop until the stopping criterion is reached
I would like to highlight the key features of this algorithm, which are that champions do not participate in duels, but rather train new duelists, and the element of randomness through the luck coefficient makes duels unpredictable. Differentiated approach: losers learn, and winners seek innovative solutions. The population size remains constant through elimination of the worst-performing duelists. Now we can move on to the implementation of the DA_duelist algorithm code.
Let's write the C_AO_DA_duelist class, which inherits the C_AO class, initializes an array of parameters (params) and assigns them names and default values.
- SetParams () — changes the values of internal variables (popSize, luckCoefficient, etc.) based on the values stored in the "params" array.
- Init () — algorithm initialization method. It accepts arrays to define ranges and steps for parameters, as well as the number of epochs.
- Moving ()— a method that contains the main logic for moving the "duelists" (searching for optimal parameters).
- Revision () — responsible for analyzing results and making adjustments to the process.
Variables:
- luckCoefficient — luck coefficient;
- learningProbability — learning probability;
- innovationProbability — innovation probability;
- championsCount — number of champions;
- winners [] — array of indices of winning "duelists";
- losers [] — array of indices of the losing "duelists";
- champions [] — array of "champions" indices.
- DetermineWinnerAndLoser() determines the winner and loser of a "duel";
- LearningProcess () implements the learning process for the loser using the winner;
- InnovationProcess () implements the innovation process for the winner;
- TrainNewDuelist () trains a new "duelist" based on the champion.
//———————————————————————————————————————————————————————————————————— 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); }; //————————————————————————————————————————————————————————————————————
The Init method of the C_AO_DA_duelist class is intended to prepare the algorithm for execution. Its main task is to initialize the initial parameters and data structures before starting work.
The method starts by calling standard initialization, which handles ranges and steps for the parameters. If this standard initialization fails, the method returns 'false' indicating that it cannot continue. The method then adjusts the number of champions: if it is less than one, it sets it to one; if it is greater than or equal to the population size, it reduces it to a quarter of the total population size. The arrays storing the indices of winners, losers, and champions are then cleared and redistributed so that they are empty and ready for the next stage of the algorithm. At the end, the method returns 'true', signaling successful initialization.
//———————————————————————————————————————————————————————————————————— //--- 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; } //————————————————————————————————————————————————————————————————————
DetermineWinnerAndLoser method of the C_AO_DA_duelist class determines the winner and loser in a duel between two individuals (duelists) of the population. Let's consider the order of actions.
- "Luck" calculation. For each duelist (A and B), the "Luck" value (A_Luck and B_Luck) is calculated. "Luck" depends on the duelist's base f value (their "fitness"), the luckCoefficient coefficient, and a random element obtained using u.RNDprobab(). Some randomness is used to simulate the effect of luck.
- Determining the winner. The sum of the duelist's f fitness and its "luck" is compared. Whichever duelist has the greater or equal resulting value is considered the winner.
- Saving results. The winner and loser indices are added to the corresponding 'winners' and 'losers' arrays. These arrays are used for further analysis and population evolution.
//———————————————————————————————————————————————————————————————————— //--- 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; } } //————————————————————————————————————————————————————————————————————
The LearningProcess method of the C_AO_DA_duelist class implements a learning process, in which an individual that loses a duel "learns" from the winner. The purpose of this process is to improve the loser's performance by transferring some of the winner's strategy to the losing individual. Let's see what happens inside the method:
Iteration over coordinates. The "for" loop iterates over all the "coordinates" of an individual. The number of coordinates is determined by the "coords" variable.
Probabilistic copying. Inside the loop, a random check is performed using u.RNDprobab (). If the random number is less than learningProbability, the following action occurs.
Copying characteristics. A loser (loserIndex) "borrows" the value of the c coordinate from the winner (winnerIndex). This means that the losing individual's characteristic is updated to take the value of the corresponding characteristic of the winner. Essentially, the loser copies part of the winner's strategy.
Ultimately, the method simulates the transfer of knowledge or strategy from a more successful individual to a less successful one, which is the basis for the evolutionary process.
//———————————————————————————————————————————————————————————————————— //--- 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]; } } } //————————————————————————————————————————————————————————————————————
The InnovationProcess method of the C_AO_DA_duelist class implements the process of "innovation" or mutation for the individual that wins the duel. This process introduces random changes to the winner's strategy, exploring new possibilities.
Iteration over coordinates. The "for" loop iterates over all the 'c' coordinates of an individual, similar to LearningProcess.
Probabilistic mutation. If the random number generated by u.RNDprobab() is less than innovationProbability, a mutation occurs. In other words, an individual tries to change its strategy with a given chance (set by innovationProbability).
Generation and correction of a new value. The random number generator u.RNDfromCI () creates a random value for the 'c' parameter that is in the range from rangeMin[c] to rangeMax[c]. The resulting random value is adjusted using u.SeInDiSp(). This function is responsible for converting a value to a discrete set of values with a step rangeStep [c] within a given range (rangeMin [c], rangeMax [c]).
As a result, the method allows the winner to experiment with new strategies, which can potentially lead to improved characteristics of the individual and its adaptability to the environment. This is an important part of the evolutionary process.
//———————————————————————————————————————————————————————————————————— //--- 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]); } } } //————————————————————————————————————————————————————————————————————
The TrainNewDuelist method of the C_AO_DA_duelist class is responsible for creating a new duelist (individual) and its initial setup by "training" it from the current champion. It is a process of inheritance, but with elements of randomness, which allows for the introduction of genetic diversity into the population.
Iteration over coordinates. The "for" loop iterates over all the 'c' parameters (coordinates) of the new duelist (newDuelistIndex), setting each of them.
Calculating deviation. The 'deviation' is calculated, which determines the range of possible changes relative to the spread of acceptable values.
Inheritance with mutation (Gauss Distribution). A random deviation obtained using the u.GaussDistribution() function is added to the champion 'c' characteristic (championIndex). This function generates a random number from a normal distribution (Gaussian distribution), which allows for random variations in inherited characteristics. The arguments 0, rangeMin [c], rangeMax [c], 8 specify the parameters of the normal distribution: the mean value 0, the minimum and maximum values, and the parameter controlling the width of the distribution (8).
Adjustment to the discrete grid (SeInDiSp). The resulting value of the new duelist's characteristic after mutation is converted to a discrete set of values using the u.SeInDiSp() function. This function ensures that the new duelist's parameters are within the allowed range and correspond to the rangeStep[c] step.
As a result, the new duelist receives stats similar to those of the champion, but with random variations, which creates diversity in the population and allows for the exploration of new strategies.
//———————————————————————————————————————————————————————————————————— //--- 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]); } } //————————————————————————————————————————————————————————————————————
The Moving method of the C_AO_DA_duelist class represents the main step of the duelist evolution algorithm. It includes initialization (at first launch), reproduction, selection, duels, training (using the results of duels), innovation and culling. This is the heart of the evolutionary process that happens inside the method:
Initialization of the population (first run). If this is the first run (revision is 'false'), the population is initialized: for each individual i, values for all characteristics j are randomly assigned according to the allowed range and step. After initialization, 'revision' is set to 'true' to avoid reinitialization on subsequent runs.
Preparing for reproduction. The size of 'a' array (representing the population) increases to accommodate new duelists spawned from champions. New duelists are initialized (initial states are set).
Selecting champions. The population is sorted by the f (fitness) parameter to select the best individuals. The champions are determined - the best championsCount individuals. Each champion uses the TrainNewDuelist method to create a new duelist that inherits the champion's traits.
Preparing for duels. The "winners" and "losers" arrays are cleared to prepare for new duel results.
Conducting duels. Each individual, except champions, fights one random opponent (also not a champion). The outcome of each duel (who wins, who loses) is determined using DetermineWinnerAndLoser.
Duelist training (Learning). Losers learn from winners. LearningProcess allows losers to change their parameters by adopting the best traits of winners.
Innovations of winners. The best individuals use InnovationProcess to try to change their strategy and explore new possibilities.
Re-sorting. The entire population is sorted again by 'f' parameter to update the order of individuals after changes made through duels, training, and innovation.
Culling the worst. The size of the 'a' array is reset to popSize, removing the worst individuals to maintain a constant population size.
This cycle represents one step of evolution, in which offspring are created, the best are selected, learning occurs, strategies are updated, and the population size is maintained. The Moving method performs the basic evolutionary operations: reproduction, mutation, selection and learning.
//———————————————————————————————————————————————————————————————————— //--- 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); } //————————————————————————————————————————————————————————————————————
The Revision method of the C_AO_DA_duelist class is used to update information about the best solution found in the current population. It is designed to track the global optimum during the evolution.
Iteration over the population. The "for" loop iterates over each individual in the population (i from 0 to popSize).
Fitness comparison. Inside the loop, we check whether the 'f' fitness of the current individual is better than the current best fitness of fB.
Update the best solution. If (a[i].f) is better than (fB), then (fB) is updated, that is, (fB) remembers the best fitness found so far. In addition, the 'c' characteristics of the current individual are copied into the cB array. This means that cB stores the characteristic values of the best individual found so far.
Ultimately, the Revision method scans the current population, finds the individual with the best fitness, and if this individual is better than the current "record", it updates the information about the best found individual (its fitness and the characteristics themselves). This method is necessary to track the progress of the algorithm and find the global optimum.
//———————————————————————————————————————————————————————————————————— //--- 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); } } } //————————————————————————————————————————————————————————————————————
Test results
Overall, after the research conducted, the duelist algorithm performs quite well overall.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%)
The visualization of the algorithm operation shows a small spread in values for both small and large dimensions, which indicates the good search capabilities of the algorithm.

DA_duelist on the Hilly test function

DA_duelist on the Forest test function

DA_duelist on the Megacity test function
Based on the test results, the Duelist algorithm ranks 42nd overall among the tested optimization algorithms.
| # | 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 | ANS | across neighbourhood search | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 2 | CLA | 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 |
| 3 | 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 |
| 4 | (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 |
| 5 | 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 |
| 6 | 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 |
| 7 | 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 |
| 8 | 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 |
| 9 | 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 |
| 10 | 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 |
| 11 | 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 |
| 12 | 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 |
| 13 | 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 |
| 14 | 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 |
| 15 | 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 |
| 16 | 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 |
| 17 | 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 |
| 18 | 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 |
| 19 | 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 |
| 20 | 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 |
| 21 | 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 |
| 22 | 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 |
| 23 | 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 |
| 24 | CRO | chemical reaction optimization | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 25 | BIO | 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 |
| 26 | 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 |
| 27 | 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 |
| 28 | 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 |
| 29 | 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 |
| 30 | 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 |
| 31 | 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 |
| 32 | (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 |
| 33 | 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 |
| 34 | 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 |
| 35 | 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 |
| 36 | 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 |
| 37 | 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 |
| 38 | 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 |
| 39 | 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 |
| 40 | 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 |
| 41 | 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 |
| 42 | 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 |
| 43 | BFO-GA | bacterial foraging optimization - ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| 44 | SOA | simple optimization algorithm | 0.91520 | 0.46976 | 0.27089 | 1.65585 | 0.89675 | 0.37401 | 0.16984 | 1.44060 | 0.69538 | 0.28031 | 0.10852 | 1.08422 | 4.181 | 46.45 |
| 45 | ABHA | artificial bee hive algorithm | 0.84131 | 0.54227 | 0.26304 | 1.64663 | 0.87858 | 0.47779 | 0.17181 | 1.52818 | 0.50923 | 0.33877 | 0.10397 | 0.95197 | 4.127 | 45.85 |
| 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 | |
Summary
Duelist Algorithm demonstrates respectable results in the field of metaheuristic optimization, ranking among the top 45 population-based algorithms. While it does not claim to be the absolute champion among optimization methods, its key advantage is speed.
Adaptive learning strategy – in the context of trading system optimization, this means that unsuccessful parameter configurations quickly adopt successful patterns, which is especially valuable when working with non-stationary financial series.
A balance of exploitation and exploration — winners do not rest on their laurels, but continue to seek improvement through innovation. This is critical for trading strategies that must adapt to changing market conditions.
The element of controlled randomness — the luck factor — naturally models market uncertainty, where even a good strategy can temporarily perform poorly due to short-term volatility.
A hierarchical structure with champions ensures the best solutions are preserved and disseminated, which in trading is equivalent to preserving time-tested strategies while simultaneously seeking new opportunities.
The Duelist Algorithm is a solid solution that, through the metaphor of martial arts, brings a simple yet effective idea to the world of optimization: learn from the strong, experiment in a position of strength, and pass on knowledge to the next generation.

Figure 2. Color gradation of algorithms across the corresponding tests

Figure 3. Histogram of algorithm testing results (scale from 0 to 100, the higher the better, where 100 is the maximum possible theoretical result, in the archive there is a script for calculating the rating table)
DA_duelist pros and cons:
Pros:
- Fast.
- Low variance across the test functions.
Cons:
- Low convergence accuracy.
An archive with the latest versions of the algorithm code is attached to the article. The author of the article is not responsible for the absolute accuracy in the description of canonical algorithms. Changes have been made to many of them to improve search capabilities. The conclusions and judgments presented in the articles are based on the results of the experiments.
Programs used in the article
| # | Name | Type | Description |
|---|---|---|---|
| 1 | #C_AO.mqh | Include | Parent class of population optimization algorithms |
| 2 | #C_AO_enum.mqh | Include | Enumeration of population optimization algorithms |
| 3 | TestFunctions.mqh | Include | Library of test functions |
| 4 | TestStandFunctions.mqh | Include | Test stand function library |
| 5 | Utilities.mqh | Include | Library of auxiliary functions |
| 6 | CalculationTestResults.mqh | Include | Script for calculating results in the comparison table |
| 7 | Testing AOs.mq5 | Script | The unified test stand for all population optimization algorithms |
| 8 | Simple use of population optimization algorithms.mq5 | Script | A simple example of using population optimization algorithms without visualization |
| 9 | Test_AO_DA_duelist.mq5 | Script | DA_duelist test stand |
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/19093
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Automatic Session Volume Profile Builder in MQL5: Rendering POC and Value Area Without Third-Party Tools
Code, Tears, and Algo Forge
MQL5 Wizard Techniques you should know (Part 100): Sliding Window Median and Bidirectional LSTM for a Custom Trailing Stop
Implementation of the Quantum Reservoir Computing (QRC) circuit
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
An article entitled ‘The Duelist Algorithm’ has been published:
Author: Andrey Dik
Foil fencers fighting in the ring. The market isn’t a ring, but a terrain. One moment you’re up to your waist in a swamp. On the ice, the figure skater will win, but in the swamp… And in the end, what sort of warrior will emerge?
You can look it up for yourselves online; the author of this thread didn’t make it up.