Algoritmo do Duelista - Duelist Algorithm
Conteúdo
Introdução
No trading algorítmico, cada milissegundo pode representar lucro ou prejuízo, por isso a busca pelos parâmetros ideais de uma estratégia de trading se torna uma tarefa crítica. Atualmente, os traders recorrem a métodos de inteligência artificial e a algoritmos evolucionários para ajustar seus sistemas de trading.
Hoje vamos analisar uma nova abordagem de otimização: o Duelist Algorithm (Algoritmo do Duelista), inspirado na antiga arte dos duelos. O algoritmo foi desenvolvido em 2015 por um grupo de cientistas indonésios liderado por Biyanto como alternativa aos algoritmos evolucionários tradicionais, com o objetivo de reduzir o caráter "cego" dos operadores de mutação e crossover por meio de uma abordagem diferenciada para vencedores e perdedores. Neste artigo, vamos examinar em detalhes a base matemática do Duelist Algorithm, implementar o algoritmo em MQL5 e realizar uma análise comparativa com outros métodos de otimização populacional.
Implementação do algoritmo
Imagine o ambiente de negociação como uma arena em que diferentes estratégias competem constantemente entre si. Algumas vencem em determinadas condições de mercado, outras saem derrotadas. Mas o que torna um trader bem-sucedido? A capacidade de aprender tanto com as vitórias quanto com as derrotas, adaptando e aprimorando continuamente a própria abordagem.
É justamente essa ideia que o Duelist Algorithm coloca em prática. Ao contrário dos algoritmos genéticos clássicos, nos quais todos os indivíduos são tratados da mesma forma, o algoritmo do duelista reconhece a diferença fundamental entre vencedores e perdedores. Os perdedores aprendem: analisam as estratégias dos vencedores e incorporam seus elementos mais eficazes. Já os vencedores tendem à inovação: experimentam novas abordagens, sabendo que sua estratégia-base já demonstrou eficiência. Os campeões atuam como mentores: as melhores estratégias são transmitidas adiante.

Figura 1. Ilustração do funcionamento do algoritmo DA_duelist
A visualização das principais etapas do algoritmo mostra todos os momentos-chave do funcionamento do Duelist Algorithm: população inicial, em que todos os duelistas começam com chances iguais; definição dos campeões, em que os melhores duelistas são destacados em dourado. Duelos, em que o confronto é representado e o vencedor e o perdedor são identificados. Aprendizado e inovação, dois caminhos distintos de evolução para perdedores e vencedores. Treinamento de novos duelistas, quando os campeões transmitem suas habilidades à nova geração; e eliminação, em que os piores duelistas são removidos da população.
A ilustração também inclui uma seta cíclica que mostra a natureza iterativa do algoritmo, o resultado final com o campeão vencedor, além de uma legenda detalhada com a explicação de todos os elementos e parâmetros-chave. A visualização ajuda a compreender rapidamente o conceito central do algoritmo.
Agora, após essa análise detalhada, vamos escrever o pseudocódigo do algoritmo.
PARÂMETROS DE ENTRADA:
- popSize: tamanho da população de duelistas
- luckCoefficient: coeficiente de sorte nos duelos
- learningProbability: probabilidade de aprendizado do perdedor
- innovationProbability: probabilidade de inovação do vencedor
- championsCount: número de campeões
INICIALIZAÇÃO:
1. Verificar e ajustar championsCount:
- Se < 1, definir como 1
- Se >= popSize, definir como popSize / 4
2. Criar arrays vazios winners, losers
3. Criar um array champions de tamanho championsCount
LAÇO PRINCIPAL (Moving):
SE for a primeira iteração:
PARA cada duelista i de 0 a popSize:
PARA cada coordenada j:
Inicializar com um valor aleatório dentro dos limites permitidos
Definir a flag revision = true
SAIR da função
CASO CONTRÁRIO (iterações seguintes):
1. EXPANSÃO DA POPULAÇÃO:
- Aumentar o array de duelistas para o tamanho (popSize + championsCount)
- Inicializar as estruturas dos novos duelistas
2. DEFINIÇÃO DOS CAMPEÕES:
- Ordenar os duelistas por fitness decrescente (bubble sort)
- Os primeiros championsCount duelistas se tornam campeões
3. TREINAMENTO DE NOVOS DUELISTAS:
PARA cada campeão i:
Chamar TrainNewDuelist (i, popSize + i):
PARA cada coordenada c:
novo_duelista [c] = campeão [c] + GaussDistribution ()
Aplicar os limites do intervalo
4. EXECUÇÃO DOS DUELOS:
Limpar os arrays winners e losers
PARA cada duelista i de championsCount até totalDuelists:
Escolher um oponente aleatório opponent
SE opponent != i:
DetermineWinnerAndLoser(i, opponent):
A_Luck = fitness [A] * (luckCoef + random () * luckCoef)
B_Luck = fitness [B] * (luckCoef + random () * luckCoef)
SE (fitness [A] + A_Luck) >= (fitness [B] + B_Luck):
A é o vencedor, B é o perdedor
CASO CONTRÁRIO:
B é o vencedor, A é o perdedor
Adicionar aos arrays winners e losers
5. APRIMORAMENTO:
a) Aprendizado dos perdedores:
PARA cada par (perdedor, vencedor):
LearningProcess (loser, winner):
PARA cada coordenada c:
SE random () < learningProbability:
loser [c] = winner [c]
b) Inovação dos vencedores:
PARA cada vencedor:
InnovationProcess (winner):
PARA cada coordenada c:
SE random () < innovationProbability:
winner [c] = valor_aleatório_no_intervalo
6. ELIMINAÇÃO:
- Ordenar todos os duelistas por fitness decrescente
- Manter apenas os primeiros popSize duelistas
ATUALIZAÇÃO (Revision):
PARA cada duelista i:
SE fitness [i] > melhor_global:
Atualizar melhor_global = fitness [i]
Salvar as coordenadas da melhor solução
REPETIR o laço principal até atingir o critério de parada
Vale destacar as principais características deste algoritmo: os campeões não participam dos duelos, mas treinam novos duelistas, e o elemento de aleatoriedade introduzido pelo coeficiente de sorte torna os duelos imprevisíveis. A abordagem é diferenciada: os perdedores aprendem, enquanto os vencedores buscam soluções inovadoras. A população mantém tamanho constante por meio da eliminação dos piores indivíduos. Agora podemos avançar para a implementação em código do algoritmo DA_duelist.
Vamos escrever a classe "C_AO_DA_duelist", que herda da classe "C_AO", inicializa o array de parâmetros (params) e atribui nomes e valores padrão aos parâmetros.
- SetParams () altera os valores das variáveis internas (popSize, luckCoefficient etc.) com base nos valores armazenados no array "params".
- Init () inicializa o algoritmo. Ele recebe arrays usados para definir os intervalos e os passos dos parâmetros, além do número de épocas.
- Moving () contém a lógica principal de deslocamento dos "duelistas" (busca dos parâmetros ideais).
- Revision () é responsável por analisar os resultados e ajustar a execução do algoritmo.
- luckCoefficient: coeficiente de sorte;
- learningProbability: probabilidade de aprendizado;
- innovationProbability: probabilidade de inovação;
- championsCount: número de campeões;
- winners []: array de índices dos duelistas vencedores;
- losers []: array de índices dos duelistas perdedores;
- champions []: array de índices dos campeões.
- DetermineWinnerAndLoser () determina o vencedor e o perdedor em um "duelo";
- LearningProcess () implementa o aprendizado do perdedor com base no vencedor;
- InnovationProcess () aplica inovação ao vencedor;
- TrainNewDuelist () treina um novo "duelista" com base no campeão.
//———————————————————————————————————————————————————————————————————— 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); }; //————————————————————————————————————————————————————————————————————
O método "Init" da classe "C_AO_DA_duelist" prepara o algoritmo para execução. Sua principal tarefa é inicializar os parâmetros iniciais e as estruturas de dados antes da execução.
O método primeiro chama a inicialização padrão, que processa os intervalos e os passos dos parâmetros. Se essa inicialização padrão falhar, o método retorna "false", sinalizando que não é possível continuar. Em seguida, o método ajusta o número de campeões: se ele for menor que um, define-o como um; se for maior ou igual ao tamanho da população, reduz esse número para um quarto do tamanho total da população. Depois, os arrays que armazenam os índices dos vencedores, perdedores e campeões são limpos e realocados, ficando vazios e prontos para a próxima etapa do algoritmo. No fim, o método retorna "true", sinalizando que a inicialização foi concluída com sucesso.
//———————————————————————————————————————————————————————————————————— //--- Инициализация 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; } //————————————————————————————————————————————————————————————————————
O método "DetermineWinnerAndLoser" da classe "C_AO_DA_duelist" determina o vencedor e o perdedor em um duelo entre dois indivíduos (duelists) da população. Vamos analisar a sequência de ações.
- Cálculo da "sorte". Para cada duelista (A e B), é calculado um valor de "sorte" (A_Luck e B_Luck). A "sorte" depende do valor-base "f" do duelista (sua "aptidão"), do coeficiente "luckCoefficient" e de um elemento aleatório obtido por meio de "u.RNDprobab ()". Um certo grau de aleatoriedade é usado para simular o efeito da sorte.
- Determinação do vencedor. Compara-se a soma da aptidão "f" de cada duelista com sua respectiva "sorte". Aquele cuja soma for maior ou igual é considerado o vencedor.
- Armazenamento dos resultados. Os índices do vencedor e do perdedor são adicionados aos arrays correspondentes "winners" e "losers". Esses arrays são usados na análise posterior e na evolução da população.
//———————————————————————————————————————————————————————————————————— //--- Определение победителя и проигравшего в дуэли 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; } } //————————————————————————————————————————————————————————————————————
O método "LearningProcess" da classe "C_AO_DA_duelist" implementa a rotina de aprendizado em que o indivíduo que perdeu o duelo "aprende" com o vencedor. O objetivo dessa etapa é melhorar as características do perdedor, transferindo para ele parte da estratégia do vencedor. Vejamos o que acontece dentro do método:
Iteração pelas coordenadas. O laço "for" percorre todas as "coordenadas" do indivíduo. O número de coordenadas é definido pela variável "coords".
Cópia probabilística. Dentro do laço, é feita uma verificação aleatória com "u.RNDprobab ()". Se o número aleatório for menor que "learningProbability" (probabilidade de aprendizado), ocorre a ação seguinte.
Cópia da característica. O perdedor (loserIndex) copia do vencedor (winnerIndex) o valor da coordenada "c". Isso significa que a característica do indivíduo perdedor é atualizada com o valor da característica correspondente do vencedor. Em essência, o perdedor copia parte da estratégia do vencedor.
Como resultado, o método simula a transferência de conhecimento ou estratégia de um indivíduo mais bem-sucedido para outro menos bem-sucedido, o que serve de base para o processo evolutivo.
//———————————————————————————————————————————————————————————————————— //--- Процесс обучения проигравшего у победителя 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]; } } } //————————————————————————————————————————————————————————————————————
O método "InnovationProcess" da classe "C_AO_DA_duelist" implementa a "inovação", ou mutação, no indivíduo que venceu o duelo. Essa etapa introduz alterações aleatórias na estratégia do vencedor, testando novas possibilidades.
Iteração pelas coordenadas. O laço "for" percorre todas as coordenadas "c" do indivíduo, de forma semelhante ao "LearningProcess".
Mutação probabilística. Se o número aleatório obtido por meio de "u.RNDprobab ()" for menor do que "innovationProbability", ocorre uma mutação. Em outras palavras, com uma determinada probabilidade (definida por innovationProbability), o indivíduo tenta modificar sua estratégia.
Geração e correção do novo valor. O gerador de números aleatórios "u.RNDfromCI ()" gera, para a coordenada "c", um valor aleatório no intervalo entre "rangeMin [c]" e "rangeMax [c]". O valor aleatório obtido é ajustado com "u.SeInDiSp ()". Essa função ajusta o valor a um conjunto discreto de valores com passo "rangeStep [c]" dentro do intervalo definido (rangeMin [c], rangeMax [c]).
Com isso, o método permite que o vencedor "experimente" novas estratégias, o que pode levar à melhoria das características do indivíduo e da sua adaptação ao ambiente. Essa é uma parte importante do processo evolutivo.
//———————————————————————————————————————————————————————————————————— //--- Процесс инновации для победителя 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]); } } } //————————————————————————————————————————————————————————————————————
O método "TrainNewDuelist" da classe "C_AO_DA_duelist" é responsável por criar um novo duelista (indivíduo) e configurá-lo inicialmente por meio do "aprendizado" com o campeão atual. Trata-se de um mecanismo de herança com elementos de aleatoriedade, o que permite introduzir diversidade genética na população.
Iteração pelas coordenadas. O laço "for" percorre todas as características (coordenadas) "c" do novo duelista (newDuelistIndex), ajustando cada uma delas.
Cálculo do desvio (deviation). Calcula-se a variável "deviation", que define a faixa de possíveis alterações com base na amplitude dos valores admissíveis.
Herança com mutação (Gauss Distribution). Soma-se à característica "c" do campeão (championIndex) um desvio aleatório obtido com a função "u.GaussDistribution ()". Essa função gera um número aleatório a partir de uma distribuição normal (distribuição de Gauss), permitindo introduzir alterações aleatórias nas características herdadas. Os argumentos "0", "rangeMin [c]", "rangeMax [c]" e "8" definem os parâmetros da distribuição normal: média 0, valores mínimo e máximo, e o valor 8, que controla a largura da distribuição.
Conversão para valor discreto (SeInDiSp). Após a mutação, o valor obtido para a característica do novo duelista é ajustado a um conjunto discreto de valores com a função "u.SeInDiSp ()". Essa função garante que os parâmetros do novo duelista fiquem dentro do intervalo admissível e respeitem o passo "rangeStep [c]".
Como resultado, o novo duelista recebe características próximas às do campeão, mas com alterações aleatórias, o que gera diversidade na população e permite testar novas estratégias.
//———————————————————————————————————————————————————————————————————— //--- Чемпион тренирует нового дуэлянта 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]); } } //————————————————————————————————————————————————————————————————————
O método "Moving" da classe "C_AO_DA_duelist" representa a etapa principal do algoritmo de evolução dos duelistas. Ele inclui inicialização (na primeira execução), reprodução, seleção, duelos, aprendizado (com base nos resultados dos duelos), inovação e eliminação. Esse é o núcleo do processo evolutivo. Vejamos como o método funciona:
Inicialização da população (primeira execução). Se esta for a primeira execução ("revision" for igual a false), a população é inicializada: para cada indivíduo "i", são definidos aleatoriamente os valores de todas as características "j", de acordo com o intervalo e o passo admissíveis. Após a inicialização, "revision" é definido como "true", para evitar uma nova inicialização nas execuções seguintes.
Preparação para reprodução. O tamanho do array "a" (que representa a população) é aumentado, permitindo acomodar os novos duelistas criados com base nos campeões. Os novos duelistas são inicializados, com seus estados iniciais definidos.
Seleção dos campeões. A população é ordenada pelo parâmetro "f" (fitness, ou aptidão), para selecionar os melhores indivíduos. São definidos os campeões, isto é, os "championsCount" melhores indivíduos. Cada campeão usa o método "TrainNewDuelist" para criar um novo duelista, que herda suas características.
Preparação para os duelos. Os arrays "winners" e "losers" são limpos para receber os novos resultados dos duelos.
Realização dos duelos. Cada indivíduo, exceto os campeões, enfrenta um oponente aleatório, também não campeão. O resultado de cada duelo, ou seja, quem venceu e quem perdeu, é determinado por meio de "DetermineWinnerAndLoser".
Aprendizado dos duelistas (Learning). Os perdedores aprendem com os vencedores. "LearningProcess" permite que os perdedores alterem seus parâmetros, incorporando as melhores características dos vencedores.
Inovação dos vencedores (Innovation). Os melhores indivíduos usam "InnovationProcess" para tentar modificar sua estratégia e testar novas possibilidades.
Reordenação. Toda a população é novamente ordenada pelo parâmetro "f", para atualizar a ordem dos indivíduos após as alterações introduzidas durante os duelos, o aprendizado e a inovação.
Eliminação dos piores. O array "a" volta ao tamanho "popSize", com a remoção dos piores indivíduos para manter constante o tamanho da população.
Esse laço representa uma etapa da evolução, na qual ocorrem a criação de descendentes, a seleção dos melhores, o aprendizado, a atualização das estratégias e a manutenção do tamanho da população. O método "Moving" executa as principais operações evolutivas: reprodução, mutação, seleção e aprendizado.
//———————————————————————————————————————————————————————————————————— //--- Основной шаг алгоритма 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); } //————————————————————————————————————————————————————————————————————
O método "Revision" da classe "C_AO_DA_duelist" atualiza as informações sobre a melhor solução encontrada na população atual. Ele é usado para acompanhar o ótimo global ao longo da evolução.
Iteração pela população. O laço "for" percorre cada indivíduo da população (i de 0 até popSize).
Comparação da aptidão. Dentro do laço, verifica-se se a aptidão "f" do indivíduo atual é melhor que a melhor fitness atual "fB".
Atualização da melhor solução. Se (a [i].f) for melhor que (fB), então (fB) é atualizado, ou seja, (fB) passa a armazenar o melhor fitness encontrado até o momento. Além disso, as características "c" do indivíduo atual são copiadas para o array "cB". Isso significa que "cB" armazena os valores das características do melhor indivíduo encontrado até agora.
Como resultado, o método "Revision" percorre a população atual, encontra o indivíduo com a melhor aptidão e, se esse indivíduo superar o "recorde" atual, atualiza as informações sobre o melhor indivíduo encontrado, tanto seu fitness quanto suas características. Esse método é necessário para acompanhar o progresso do algoritmo e encontrar o ótimo global.
//———————————————————————————————————————————————————————————————————— //--- Обновление лучшего и худшего решений 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); } } } //————————————————————————————————————————————————————————————————————
Resultados dos testes
De modo geral, após os testes realizados, o algoritmo do duelista apresenta bom desempenho.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%)
Na visualização do funcionamento do algoritmo, observa-se uma pequena dispersão dos valores tanto em baixa e alta dimensionalidade, o que indica boa capacidade de busca do algoritmo.

DA_duelist na função de teste Hilly

DA_duelist na função de teste Forest

DA_duelist na função de teste Megacity
De acordo com os resultados dos testes, o algoritmo do duelista ocupa o 42º lugar no ranking geral de algoritmos de otimização.
| № | 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 optimisation | 0,94629 | 0,66112 | 0,29853 | 1,90593 | 0,87906 | 0,58422 | 0,21146 | 1,67473 | 0,75846 | 0,42646 | 0,12686 | 1,31178 | 4,892 | 54,36 |
| 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 | |
Conclusões
O Duelist Algorithm apresenta bons resultados na área de otimização metaheurística, ocupando uma posição no top 45 dos algoritmos populacionais. Embora não aspire ao título de campeão absoluto entre os métodos de otimização, sua principal vantagem é a velocidade.
Estratégia adaptativa de aprendizado: no contexto da otimização de sistemas de trading, isso significa que configurações de parâmetros malsucedidas incorporam rapidamente padrões bem-sucedidos, algo especialmente valioso ao lidar com séries financeiras não estacionárias.
Equilíbrio entre intensificação e diversificação: os vencedores não se acomodam, mas continuam buscando melhorias por meio da inovação. Isso é crucial para estratégias de trading que precisam se adaptar às mudanças nas condições de mercado.
Elemento de aleatoriedade controlada: o coeficiente de sorte modela naturalmente a incerteza do mercado, em que até uma boa estratégia pode apresentar temporariamente resultados ruins devido à volatilidade de curto prazo.
Estrutura hierárquica com campeões: garante a preservação das melhores soluções e sua disseminação, o que, no trading, equivale a manter estratégias comprovadas ao longo do tempo ao mesmo tempo em que se buscam novas oportunidades.
O Duelist Algorithm é uma solução sólida que, por meio da metáfora das artes marciais, introduz no campo da otimização uma ideia simples, porém eficaz: aprender com os mais fortes, experimentar a partir de uma posição de força e transmitir conhecimento à geração seguinte.

Figura 2. Gradação de cores dos algoritmos nos testes correspondentes

Figura 3. Histograma dos resultados dos testes dos algoritmos (em escala de 0 a 100, quanto maior, melhor, em que 100 é o resultado teórico máximo possível, no arquivo há um script para calcular a tabela de classificação)
Prós e contras do algoritmo DA_duelist:
Prós:
- Rápido.
- Pequena dispersão dos resultados nas funções de teste.
Contras:
- Precisão de convergência limitada.
O artigo inclui um arquivo compactado anexo com as versões atuais dos códigos dos algoritmos. O autor do artigo não se responsabiliza pela precisão absoluta na descrição dos algoritmos canônicos, pois muitos deles foram modificados para melhorar a capacidade de busca. As conclusões e avaliações apresentadas nos artigos baseiam-se nos resultados dos experimentos realizados.
Programas usados no artigo
| # | Nome | Tipo | Descrição |
|---|---|---|---|
| 1 | #C_AO.mqh | Arquivo de inclusão | Classe base dos algoritmos populacionais de otimização |
| 2 | #C_AO_enum.mqh | Arquivo de inclusão | Enumeração dos algoritmos populacionais de otimização |
| 3 | TestFunctions.mqh | Arquivo de inclusão | Biblioteca de funções de teste |
| 4 | TestStandFunctions.mqh | Arquivo de inclusão | Biblioteca de funções da bancada de testes |
| 5 | Utilities.mqh | Arquivo de inclusão | Biblioteca de funções auxiliares |
| 6 | CalculationTestResults.mqh | Arquivo de inclusão | Script para calcular os resultados da tabela comparativa |
| 7 | Testing AOs.mq5 | Script | Bancada de testes unificada para todos os algoritmos populacionais de otimização |
| 8 | Simple use of population optimization algorithms.mq5 | Script | Exemplo simples de uso dos algoritmos populacionais de otimização sem visualização |
| 9 | Test_AO_DA_duelist.mq5 | Script | Bancada de testes para o DA_duelist |
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/19093
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Caminhe em novos trilhos: Personalize indicadores no MQL5
Redes neurais em trading: decomposição em vez de escalonamento (Conclusão)
Está chegando o novo MetaTrader 5 e MQL5
Técnicas do MQL5 Wizard que você deve conhecer (Parte 58): Aprendizado por Reforço (DDPG) com Padrões de Média Móvel e Oscilador Estocástico
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Foi publicado o artigo “Algoritmo do Duelista — Duelist Algorithm”:
Autor: Andrey Dik
Duellistas que lutam no ringue. O mercado não é um ringue, mas um terreno. Às vezes na areia, às vezes atolados até a cintura no pântano. No gelo, o patinador artístico vence, mas no pântano... E, no fim das contas, que tipo de guerreiro vai sair disso?
Vocês mesmos podem conferir na internet, não foi o autor do tópico que inventou isso