Русский
preview
Algoritmo do mercado acionário: Exchange Market Algorithm (EMA)

Algoritmo do mercado acionário: Exchange Market Algorithm (EMA)

MetaTrader 5Sistemas de negociação |
15 0
Andrey Dik
Andrey Dik


Conteúdo

  1. Introdução
  2. Implementação do algoritmo
  3. Resultados dos testes


Introdução

Na área de otimização numérica, novos algoritmos vêm sendo desenvolvidos continuamente com o objetivo de resolver de forma eficiente problemas complexos em condições de incerteza e alta dimensionalidade. Entre eles, um lugar de destaque é ocupado pelas meta-heurísticas populacionais que, ao imitarem processos naturais ou sociais, demonstram capacidade impressionante de encontrar ótimos globais.

No entanto, nem todo algoritmo novo consegue alcançar um nível competitivo de desempenho. Este artigo é dedicado a uma análise detalhada do algoritmo Exchange Market Algorithm (EMA), representante da classe de métodos mencionada acima, inspirado no comportamento de traders no mercado acionário. O algoritmo modela o processo de negociação de ações, em que participantes do mercado com diferentes níveis de sucesso aplicam estratégias variadas para maximizar o lucro. 


Implementação do algoritmo

O algoritmo inicia seu funcionamento formando uma população de participantes do mercado (traders), e todos os participantes são divididos em três grupos iguais. Cada trader recebe aleatoriamente uma posição inicial no espaço de busca, que representa sua carteira atual de ações. Essas posições são distribuídas uniformemente por toda a faixa de valores permitida.

Após a avaliação inicial do sucesso de cada trader (cálculo do valor da função objetivo), toda a população é classificada em ordem decrescente de sucesso e dividida em três grupos:

  • o primeiro grupo é a elite. São os participantes do mercado mais bem-sucedidos, que encontraram as posições mais lucrativas. Uma característica importante é que os traders de elite mantêm suas posições inalteradas durante toda a sessão de negociação, servindo de referência para os demais. 
  • o segundo grupo é a classe média. São participantes moderadamente bem-sucedidos, que buscam melhorar suas posições aprendendo com a elite, mas ao mesmo tempo estão dispostos a assumir risco moderado. 
  • o terceiro grupo é o dos iniciantes. São os participantes menos bem-sucedidos, dispostos a assumir alto risco na tentativa de melhorar radicalmente sua situação.

Cada iteração do algoritmo representa uma sessão de negociação composta por duas fases, correspondentes a diferentes estados do mercado.

Fase 1: Mercado equilibrado (negociação estável) 


Em condições de mercado estável, os traders aplicam estratégias conservadoras baseadas na imitação de participantes mais bem-sucedidos. 

Comportamento da classe média. Cada trader do segundo grupo escolhe aleatoriamente um mentor da elite e desloca gradualmente sua posição na direção dele. O grau desse deslocamento é determinado pelo coeficiente de absorção (r1 = 1.5), que diminui de forma adaptativa à medida que o algoritmo avança. O movimento não ocorre de forma determinística, pois é usado um fator aleatório que simula a incerteza das decisões de mercado. 

Comportamento dos iniciantes. Os traders do terceiro grupo ficam sob a influência tanto da elite quanto da classe média. Cada iniciante escolhe um mentor do primeiro grupo e um do segundo grupo, e sua nova posição é formada como uma combinação de movimentos na direção de ambos os mentores. Isso reflete a tentativa de aprender ao mesmo tempo com os mais bem-sucedidos e com aqueles que melhoraram suas posições recentemente.

Fase 2: Mercado oscilante (negociação volátil)


Em condições de mercado instável, os traders aplicam estratégias mais arriscadas para buscar novas oportunidades. 

Comportamento exploratório da classe média. Os traders do segundo grupo aplicam uma estratégia dupla. Com probabilidade de 50%, eles podem copiar integralmente a posição da melhor solução global (o trader mais bem-sucedido de toda a história). Nos demais casos, exploram a região ao redor do centro de massa do grupo de elite, adicionando um ruído aleatório controlado a essa posição. A magnitude do ruído é determinada pelo fator de risco (riskAlpha = 0.3) e diminui com o tempo.

Comportamento agressivo dos iniciantes. O terceiro grupo aplica as estratégias mais arriscadas:

  • com probabilidade igual ao fator de risco (30%), o trader esquece completamente sua posição atual e recomeça a partir de um ponto aleatório;
  • em 35% dos casos, é realizada uma busca ampla ao redor da posição atual com um raio grande;
  • nos casos restantes, é aplicado o aprendizado por oposição: o trader se move na direção oposta ao centro das piores soluções, tentando se afastar das regiões malsucedidas.

A principal característica do EMA é sua adaptabilidade. À medida que as iterações avançam, é calculado um coeficiente de atenuação com base em uma função sigmoide. Esse coeficiente garante uma transição suave da diversificação (no início da execução) para a intensificação das boas soluções encontradas (no final).

Depois da execução de ambas as fases, ocorre uma etapa importante: as posições de todos os traders, exceto os da elite, são atualizadas de acordo com os novos valores calculados. A elite mantém suas posições, garantindo a preservação das melhores soluções encontradas.

Em seguida, é calculado o novo sucesso de todos os traders (valor da função objetivo), a população é classificada novamente, e a nova melhor solução global é determinada. Agora vamos ver como as fórmulas do algoritmo aparecem na imagem apresentada abaixo.

ema_illustracion

A ilustração contém todas as fórmulas-chave de atualização de posições para cada grupo em ambos os regimes de mercado. Com base na descrição e nas fórmulas apresentadas, vamos montar o pseudocódigo do algoritmo EMA.

1. Inicialização

  • Criar uma população de N = 60 traders (N múltiplo de 3)
  • Definir os parâmetros: r₁ = 1.5, r₂ = 0.8, α = 0.3 (fator de risco)
  • Inicializar cada trader com uma posição aleatória
  • Definir o tamanho do grupo: m = N/3 = 20

2. Laço principal

Para cada iteração t = 1, 2, ..., T executar:

2.1 Classificação

  • Avaliar o fitness de todos os traders
  • Classificar em ordem decrescente de fitness
  • Dividir em grupos:
    • G₁ = {1, ..., m} elite
    • G₂ = {m+1, ..., 2m} classe média
    • G₃ = {2m+1, ..., N} iniciantes

2.2 Adaptação dos parâmetros

  • progress = t / T
  • decay = 1 / (1 + exp(-10(progress - 0.5)))
  • r₁ᵃᵈᵃᵖᵗ = r₁ × (1 - decay × 0.5)
  • r₂ᵃᵈᵃᵖᵗ = r₂ × (1 - decay × 0.3)

2.3 Mercado equilibrado

  • G₁: as posições são mantidas
  • G₂: para cada i ∈ G₂
    • escolher um líder aleatório k ∈ G₁
    • xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ - xᵢ)
  • G₃: para cada i ∈ G₃
    • escolher o líder k₁ ∈ G₁ e k₂ ∈ G₂
    • xᵢ = xᵢ + rand() × r₁ᵃᵈᵃᵖᵗ × (xₖ₁ - xᵢ) + rand() × r₂ᵃᵈᵃᵖᵗ × (xₖ₂ - xᵢ)

2.4 Mercado oscilante

  • G₂: para cada i ∈ G₂
    • se rand() < 0.5: xᵢ = xᵇᵉˢᵗ
    • caso contrário: xᵢ = centro(G₁) + ruído × α × (1 - decay × 0.5)
  • G₃: para cada i ∈ G₃
    • se rand() < α: xᵢ = posição_aleatória()
    • caso contrário, se rand() < 0.5 + α/2: xᵢ = xᵢ + grande_perturbação
    • caso contrário: xᵢ = 2xᵢ - centro(piores) + pequeno_ruído

2.5 Atualização

  • Aplicar as novas posições para G₂ e G₃
  • Atualizar a melhor solução global

3 Retorno do resultado

Retornar a melhor solução encontrada xᵇᵉˢᵗ

Agora podemos escrever a classe "C_AO_EMA", que será derivada da classe "C_AO" e implementará nosso algoritmo de otimização EMA. Os componentes principais são: popSize, tamanho da população (quantidade de agentes), r1, r2, coeficientes de absorção para diferentes grupos de agentes, riskAlpha, fator de risco. Em seguida, os parâmetros de configuração do algoritmo são inicializados e recebem valores padrão.

Funções:

  • SetParams () atualiza os valores dos parâmetros do algoritmo a partir do array "params", além de verificar a validade desses parâmetros para garantir que estejam dentro dos limites permitidos.
  • Init () inicializa o algoritmo antes de seu uso, recebendo parâmetros como os limites de busca (rangeMinP, rangeMaxP), o passo de variação dos parâmetros (rangeStepP) e o número de épocas (epochsP).
  • Moving () é responsável pela movimentação dos agentes no espaço de busca e representa a lógica principal do algoritmo EMA para atualizar as posições dos agentes.
  • Revision() realiza a revisão (revision) das estratégias dos agentes e inclui mecanismos para melhorar e alterar as posições atuais dos agentes.
  • GetDecayRate() retorna o coeficiente de atenuação (decay rate).

Variáveis:

  • r1, coeficiente de absorção para um grupo de agentes. Quanto maior o valor, mais fortemente os agentes desse grupo são "atraídos" por outros agentes ou pelas "melhores" soluções.
  • r2, coeficiente de absorção para outro grupo de agentes.
  • riskAlpha, fator de risco, ele determina o quanto os agentes estão expostos ao risco ao tomar decisões.
  • groupSize, tamanho de cada um dos três grupos em que a população é dividida (popSize / 3).
  • currentEpoch, número da iteração atual do algoritmo.
  • totalEpochs, número total de iterações que serão executadas pelo algoritmo.
  • tempPop, array temporário para armazenar as novas posições dos agentes durante sua movimentação. O tipo "S_AO_Agent" define a estrutura de um agente individual (solução).

A classe "C_AO_EMA" fornece uma implementação do algoritmo EMA para otimização numérica. O algoritmo divide a população em grupos com estratégias diferentes (definidas pelos coeficientes r1 e r2) e usa o fator de risco (riskAlpha) para equilibrar entre a diversificação do espaço de busca e a intensificação das "boas" soluções encontradas. As funções "Moving ()" e "Revision ()" contêm a lógica principal de otimização, "Init ()" inicializa o algoritmo, e "SetParams ()" permite alterar os valores dos parâmetros.

//————————————————————————————————————————————————————————————————————
class C_AO_EMA : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_EMA () { }
  C_AO_EMA ()
  {
    ao_name = "EMA";
    ao_desc = "Exchange Market Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/18605";

    popSize   = 60;     // Размер популяции
    r1        = 1.5;    // Коэффициент поглощения для группы 2
    r2        = 0.8;    // Коэффициент поглощения для группы 3
    riskAlpha = 0.3;    // Фактор риска

    ArrayResize (params, 4);

    params [0].name = "popSize";   params [0].val = popSize;
    params [1].name = "r1";        params [1].val = r1;
    params [2].name = "r2";        params [2].val = r2;
    params [3].name = "riskAlpha"; params [3].val = riskAlpha;
  }

  void SetParams ()
  {
    popSize   = (int)params [0].val;
    r1        = params      [1].val;
    r2        = params      [2].val;
    riskAlpha = params      [3].val;

    // Проверка корректности параметров
    if (popSize < 6) popSize = 6;
    if (popSize % 3 != 0) popSize = ((popSize / 3) + 1) * 3; // Кратно 3
    if (r1 < 0.0) r1 = 0.0;
    if (r2 < 0.0) r2 = 0.0;
    if (riskAlpha < 0.0) riskAlpha = 0.0;
    if (riskAlpha > 1.0) riskAlpha = 1.0;
  }

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

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double r1;         // Коэффициент поглощения 1
  double r2;         // Коэффициент поглощения 2
  double riskAlpha;  // Фактор риска

  private: //---------------------------------------------------------
  int    groupSize;     // размер каждой группы (popSize/3)
  int    currentEpoch;  // текущая эпоха
  int    totalEpochs;   // общее количество эпох

  S_AO_Agent tempPop []; // временная популяция для хранения новых позиций

  double GetDecayRate   ();
};
//————————————————————————————————————————————————————————————————————

O método de inicialização "Init" foi projetado para preparar o algoritmo para a execução. Ele recebe arrays de valores mínimos, valores máximos e passos para os parâmetros de busca, além do número de épocas (iterações). Primeiro, é chamada a função de inicialização padrão, responsável por configurar os intervalos de busca e verificar sua correção. Se ela retornar um resultado negativo, a inicialização do método também é interrompida.

Em seguida, é calculado o tamanho de cada grupo de agentes, já que a população é dividida em três partes iguais em número de elementos (divisão do tamanho total da população por 3). O contador da época atual é inicializado, e o número total de épocas para as quais o algoritmo foi definido é armazenado. Depois disso, é alocada memória para a população temporária, um array de agentes com o mesmo tamanho da população principal. Em um laço, cada agente desse array temporário é inicializado com o auxílio das coordenadas definidas no array "coords".

Quando todas essas etapas são concluídas com sucesso, é retornado o valor "true", o que indica que a preparação para a execução do algoritmo foi bem-sucedida.

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

  //------------------------------------------------------------------
  groupSize    = popSize / 3;
  currentEpoch = 0;
  totalEpochs  = epochsP;

  // Инициализация временной популяции
  ArrayResize (tempPop, popSize);
  for (int i = 0; i < popSize; i++)
  {
    tempPop [i].Init (coords);
  }

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

O método "Moving" é a função principal que implementa a evolução dos agentes no algoritmo EMA. Ele é responsável por alterar as posições dos agentes no espaço de busca. Inicialização inicial (executada apenas uma vez):

  • Se esta for a primeira execução do algoritmo, as coordenadas de todos os agentes são inicializadas aleatoriamente dentro dos intervalos definidos (rangeMin, rangeMax).
  • As coordenadas de cada agente são ajustadas levando em conta o passo de discretização (rangeStep).
  • A variável "revision" é definida como verdadeira, para que nas próximas chamadas desse método a inicialização não se repita.

Atualização da época e do coeficiente de atenuação:

  • O contador da época atual (currentEpoch) é incrementado.
  • É calculado o "decayRate" (coeficiente de atenuação), que diminui a cada época, influenciando o comportamento adaptativo do algoritmo.

Cópia da população atual. As coordenadas e os valores da função objetivo "f" de todos os agentes da população atual "a2 são copiados para a população temporária "tempPop", o que é feito para que as alterações em "tempPop" não afetem "a" até a conclusão de todos os cálculos na iteração atual.

FASE 1: Mercado equilibrado (Operadores de absorção). Esta fase modela o comportamento de agentes que "absorvem" informação dos melhores agentes. A população é dividida em três grupos:

  • Grupo 1 (Elite): esses agentes (com índices de 0 a groupSize - 1) não são alterados nesta etapa, são os melhores agentes, e suas posições são mantidas.
  • Grupo 2 (Operador de absorção 1): os agentes desse grupo (com índices de "groupSize" a "2*groupSize - 1") atualizam suas posições.
    • Para cada agente desse grupo, um "líder" da Grupo 1 é escolhido aleatoriamente.
    • As coordenadas do agente do Grupo 2 são deslocadas na direção das coordenadas do líder escolhido da Grupo 1. O deslocamento depende do coeficiente adaptativo "r1", que diminui levando em conta o "decayRate".
    • As coordenadas são ajustadas aos intervalos e ao passo.
  • Grupo 3 (Operador de absorção 2): os agentes desse grupo (com índices de "2 * groupSize" a "popSize - 1") também atualizam suas posições, mas de forma mais ativa.
    • Para cada agente desse grupo, dois líderes são escolhidos aleatoriamente: um da Grupo 1 e outro da Grupo 2.
    • As coordenadas do agente do Grupo 3 são deslocadas na direção de ambos os líderes escolhidos, usando o mesmo coeficiente adaptativo "r1".
    • As coordenadas são ajustadas aos intervalos e ao passo.

FASE 2: Mercado oscilante (Operadores de busca). Esta fase modela o comportamento exploratório dos agentes, permitindo que eles procurem novas áreas no espaço.

  • Grupo 2 (Operador de busca 1, risco moderado):
    • Cada coordenada do agente desse grupo é atualizada.
    • Com probabilidade de 50%, a coordenada é totalmente substituída pelo valor de "cB".
    • Caso contrário, a coordenada é deslocada em relação ao "centro da elite" (valor médio das coordenadas dos agentes do Grupo 1) com adição de ruído. O ruído depende de "riskAlpha", da faixa das coordenadas e de "decayRate".
    • As coordenadas são ajustadas aos intervalos e ao passo.
  • Grupo 3 (Operador de busca 2, alto risco):
    • Cada coordenada do agente desse grupo é atualizada.
    • Com probabilidade igual a "riskAlpha", a coordenada é reinicializada aleatoriamente dentro do intervalo. Isso modela uma diversificação muito arriscada e em larga escala.
    • Caso contrário, se o segundo número aleatório for menor que "0.5 + riskAlpha / 2.0", ocorre uma "busca ampla": a coordenada é deslocada por uma quantidade aleatória dentro de um determinado raio, que depende de "riskAlpha" e de "decayRate".
    • Caso contrário, é aplicado o "aprendizado por oposição": a coordenada é deslocada na direção oposta. Um pequeno ruído aleatório é adicionado.
    • As coordenadas são ajustadas aos intervalos e ao passo.

Cópia das alterações. Por fim, após todos os cálculos, as coordenadas atualizadas dos agentes de "tempPop" (para os Grupos 2 e 3) são copiadas de volta para a população principal "a". As coordenadas do Grupo 1 permanecem inalteradas (inicialmente, elas não foram copiadas de volta).

De modo geral, o método "Moving" implementa um processo iterativo no qual os agentes da população alteram suas posições, "aprendendo" com os melhores agentes e explorando o espaço de busca com diferentes níveis de risco. 

//————————————————————————————————————————————————————————————————————
//--- Основной цикл алгоритма
void C_AO_EMA::Moving ()
{
  // Начальная инициализация
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    revision = true;
    return;
  }

  currentEpoch++;
  double decayRate = GetDecayRate ();

  // Копирование текущей популяции во временную
  for (int i = 0; i < popSize; i++)
  {
    ArrayCopy (tempPop [i].c, a [i].c, 0, 0, WHOLE_ARRAY);
    tempPop [i].f = a [i].f;
  }

  // ФАЗА 1: Сбалансированный рынок (Поглощающие операторы)

  // Группа 1 (элита) - не изменяется
  // Индексы: 0 ... groupSize-1

  // Группа 2 - поглощающий оператор 1
  // Индексы: groupSize ... 2*groupSize-1

  double adaptiveR1 = r1 * (1.0 - decayRate * 0.5);

  for (int i = groupSize; i < 2 * groupSize; i++)
  {
    // Каждый агент группы 2 выбирает случайного лидера из группы 1
    int leaderIdx = u.RNDminusOne (groupSize);

    for (int c = 0; c < coords; c++)
    {
      tempPop [i].c [c] = a [i].c [c] + u.RNDprobab () * adaptiveR1 * (a [leaderIdx].c [c] - a [i].c [c]);
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Группа 3 - поглощающий оператор 2
  // Индексы: 2*groupSize ... popSize-1

  adaptiveR1 = r1 * (1.0 - decayRate * 0.3);

  for (int i = 2 * groupSize; i < popSize; i++)
  {
    // Выбор лидеров из групп 1 и 2
    int leader1Idx = u.RNDminusOne (groupSize);
    int leader2Idx = groupSize + u.RNDminusOne (groupSize);

    for (int c = 0; c < coords; c++)
    {
      tempPop [i].c [c] = a [i].c [c] +
                          u.RNDprobab () * adaptiveR1 * (a [leader1Idx].c [c] - a [i].c [c]) +
                          u.RNDprobab () * adaptiveR1 * (a [leader2Idx].c [c] - a [i].c [c]);

      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // ФАЗА 2: Колеблющийся рынок (Поисковые операторы)

  // Группа 2 - поисковый оператор 1 (умеренный риск)
  for (int i = groupSize; i < 2 * groupSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      double range = rangeMax [c] - rangeMin [c];

      if (u.RNDprobab () < 0.5)
      {
        tempPop [i].c [c] = cB [c];// tempPop [i].c [c] + delta;
      }
      else
      {
        // Поиск вокруг центра элиты
        double eliteCenter = 0.0;
        for (int j = 0; j < groupSize; j++)
        {
          eliteCenter += a [j].c [c];
        }
        eliteCenter /= (double)groupSize;

        double noise = riskAlpha * range * u.RNDfromCI (-0.5, 0.5) * (1.0 - decayRate * 0.5);
        tempPop [i].c [c] = eliteCenter + noise;
      }

      // Проверка границ
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Группа 3 - поисковый оператор 2 (высокий риск)
  for (int i = 2 * groupSize; i < popSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      double range = rangeMax [c] - rangeMin [c];

      if (u.RNDprobab () < riskAlpha)
      {
        // Полная реинициализация
        tempPop [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
      }
      else
        if (u.RNDprobab () < 0.5 + riskAlpha / 2.0)
        {
          // Широкий поиск
          double searchRadius = 2.0 * riskAlpha * range * (1.0 - decayRate * 0.3);
          double delta = u.RNDfromCI (-searchRadius, searchRadius);

          tempPop [i].c [c] = tempPop [i].c [c] + delta;
        }
        else
        {
          // Оппозиционное обучение
          double worstCenter = 0.0;
          int worstCount = groupSize / 2;
          for (int j = popSize - worstCount; j < popSize; j++)
          {
            worstCenter += a [j].c [c];
          }
          worstCenter /= (double)worstCount;

          // Движение в противоположном направлении от худших
          tempPop [i].c [c] = 2.0 * tempPop [i].c [c] - worstCenter;

          // Добавляем небольшой шум
          double noise = riskAlpha * range * u.RNDfromCI (-0.1, 0.1);
          tempPop [i].c [c] += noise;
        }

      // Проверка границ
      tempPop [i].c [c] = u.SeInDiSp (tempPop [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

  // Копирование из временной популяции в основную (кроме группы 1)
  for (int i = groupSize; i < popSize; i++)
  {
    ArrayCopy (a [i].c, tempPop [i].c, 0, 0, WHOLE_ARRAY);
  }
}
//————————————————————————————————————————————————————————————————————

O método "GetDecayRate" foi projetado para calcular o coeficiente de atenuação, que controla o grau de mudança no comportamento do algoritmo ao longo da execução. Primeiro, ele verifica se o número total de épocas "totalEpochs" é igual a "0" ou negativo; nesse caso, retorna zero para evitar divisão por zero e garantir uma execução segura. Em seguida, calcula o progresso da busca como a fração de épocas transcorridas em relação ao total, "progress". O valor de "progress" cresce de "0" para "1" à medida que o algoritmo avança.

Usando "progress", o método retorna um valor baseado em uma função sigmoide de forma exponencial. Essa função garante uma transição suave do coeficiente de atenuação de "0" para "1", fazendo com que no início da busca (quando "progress" está próximo de 0) o valor fique perto de "0" e, no final (quando "progress" se aproxima de 1), se aproxime de 1. Essa transição não linear ajuda a equilibrar o balanço entre a diversificação do espaço e a intensificação das soluções já encontradas.

Especificamente, é usada uma função sigmoide na forma: 1 / (1 + exp(-10 * (progress - 0.5))). Esse formato garante uma transição mais acentuada perto do meio do processo, "progress ≈ 0.5", o que favorece uma mudança mais dinâmica do coeficiente durante a execução do algoritmo.

//————————————————————————————————————————————————————————————————————
//--- Получение коэффициента затухания
double C_AO_EMA::GetDecayRate ()
{
  if (totalEpochs <= 0) return 0.0;

  // Нелинейное затухание для лучшего баланса эксплуатации/исследования
  double progress = (double)currentEpoch / (double)totalEpochs;

  // Использование сигмоидной функции для плавного перехода
  return 1.0 / (1.0 + MathExp (-10.0 * (progress - 0.5)));
}
//————————————————————————————————————————————————————————————————————

O método "Revision", no contexto do algoritmo EMA, é responsável por atualizar as melhores soluções encontradas (ótimo global) em cada iteração. É criado um array estático temporário "aT" do tipo "S_AO_Agent", e "static" significa que esse array será criado uma única vez na primeira chamada da função e manterá seu estado entre as chamadas. "ArrayResize (aT, popSize)"; altera o tamanho desse array temporário para que ele possa armazenar "popSize" (tamanho da população) agentes. Esse array será usado para a classificação. É chamada a função "Sorting" do objeto auxiliar "u". Essa função classifica a população principal de agentes "a" de acordo com sua aptidão (valor da função objetivo "f"). 

Importante: após essa operação, o array "a" será classificado de tal forma que o agente com o melhor valor da função objetivo "f" ficará na primeira posição (índice "0"). Em algoritmos de minimização, esse será o agente com o menor "f"; em algoritmos de maximização, com o maior "f". As coordenadas do melhor agente da população atual, que agora está em "a [0]" após a classificação, são copiadas para o array "cB", que armazena as coordenadas da melhor solução encontrada pelo algoritmo até o momento. O valor da função objetivo do melhor agente é atribuído à variável "fB". Essa variável armazena o valor da função objetivo correspondente às melhores coordenadas "cB".

//————————————————————————————————————————————————————————————————————
//--- Обновление лучших решений
void C_AO_EMA::Revision ()
{
  static S_AO_Agent aT []; ArrayResize (aT, popSize);
  u.Sorting (a, aT, popSize);
  ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY);
  fB = a [0].f;
}
//————————————————————————————————————————————————————————————————————


Resultados dos testes

Os resultados após a execução dos testes poderiam ser melhores, no entanto o algoritmo funciona e apresenta certas capacidades de busca.

EMA|Exchange Market Algorithm|60.0|1.5|0.8|0.3|
=============================
5 Hilly's; Func runs: 10000; result: 0.6706604188712635
25 Hilly's; Func runs: 10000; result: 0.42759923501764946
500 Hilly's; Func runs: 10000; result: 0.252217676777693
=============================
5 Forest's; Func runs: 10000; result: 0.7419215403847332
25 Forest's; Func runs: 10000; result: 0.38137087707323236
500 Forest's; Func runs: 10000; result: 0.19454127467011006
=============================
5 Megacity's; Func runs: 10000; result: 0.38769230769230767
25 Megacity's; Func runs: 10000; result: 0.21323076923076928
500 Megacity's; Func runs: 10000; result: 0.09672307692307769
=============================
All score: 3.36596 (37.40%)

Na visualização, é possível notar dispersão nos resultados em funções de baixa dimensionalidade.

Hilly

EMA na função de teste Hilly

Forest

EMA na função de teste Forest

Megacity

EMA na função de teste Megacity

Após a realização dos testes, o algoritmo EMA será apresentado em nossa tabela de classificação dos algoritmos populacionais de otimização apenas para fins de ознакомления.

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 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
13 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
14 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
15 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
16 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
17 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
18 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
19 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
20 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
21 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
22 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
23 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
24 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
25 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
26 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
27 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
28 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
29 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
30 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
31 (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
32 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
33 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
34 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
35 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
36 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
37 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
38 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
39 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
40 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
41 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
42 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
43 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
44 ACMO atmospheric cloud model optimization 0,90321 0,48546 0,30403 1,69270 0,80268 0,37857 0,19178 1,37303 0,62308 0,24400 0,10795 0,97503 4,041 44,90
45 ADAMm adaptive moment estimation M 0,88635 0,44766 0,26613 1,60014 0,84497 0,38493 0,16889 1,39880 0,66154 0,27046 0,10594 1,03794 4,037 44,85
EMA exchange_market_algorithm 0,67066 0,42759 0,25221 1,35046 0,74192 0,38137 0,19454 1,31783 0,38769 0,21323 0,09672 0,69764 3,366 37,40
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


Considerações finais

O algoritmo EMA apresentado, embora tenha capacidades básicas de busca, não demonstra desempenho suficiente para entrar no top-45 da tabela de classificação dos algoritmos populacionais de otimização. Sua estrutura monolítica e a ausência de mecanismos explícitos para superar problemas amplamente conhecidos, relacionados ao aprisionamento em extremos locais, apontam para uma série de possíveis desvantagens. 

Sem um enriquecimento substancial de sua funcionalidade e um controle adaptativo de comportamento, o EMA continuará sendo um algoritmo básico com potencial limitado para resolver problemas complexos de otimização, o que explica sua ausência no ranking das meta-heurísticas de ponta, no entanto as ideias incorporadas ao algoritmo são bastante interessantes e promissoras para a nossa coleção de ideias para aperfeiçoamentos futuros.

tab

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

chart

Figura 3. Histograma dos resultados de testes dos algoritmos (na escala de 0 a 100, quanto maior, melhor, onde 100 — resultado teórico máximo possível, no arquivo compactado o script para cálculo da tabela de classificação)

Vantagens e desvantagens do algoritmo EMA:

Vantagens:

  1. implementação simples

Desvantagens:

  1. baixa precisão de convergência

Um arquivo compactado com as versões atualizadas dos códigos dos algoritmos está anexado ao artigo. 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 as capacidades de busca. As conclusões e julgamentos apresentados nos artigos baseiam-se nos resultados dos experimentos realizados.


Programas utilizados 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 do ambiente de testes
5
Utilities.mqh
Arquivo de inclusão
Biblioteca de funções auxiliares
6
CalculationTestResults.mqh
Arquivo de inclusão
Script para cálculo dos resultados na tabela comparativa
7
Testing AOs.mq5
Script Ambiente de testes unificado para todos os algoritmos populacionais de otimização
8
Simple use of population optimization algorithms.mq5
Script
Exemplo simples de uso de algoritmos populacionais de otimização sem visualização
9
Test_AO_EMA.mq5
Script Ambiente de testes para EMA

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/18605

Arquivos anexados |
EMA.ZIP (310.01 KB)
Explorando modelos de regressão para inferência causal e trading Explorando modelos de regressão para inferência causal e trading
Neste artigo, foi realizado um estudo sobre a possibilidade de aplicar modelos de regressão no trading algorítmico. Os modelos de regressão, diferentemente da classificação binária, permitem criar estratégias de trading mais flexíveis por meio da avaliação quantitativa das variações de preço previstas.
O Filtro de Kalman para Estratégias de Reversão à Média no Forex O Filtro de Kalman para Estratégias de Reversão à Média no Forex
O filtro de Kalman é um algoritmo recursivo utilizado em trading algorítmico para estimar o verdadeiro estado de uma série temporal financeira ao filtrar o ruído dos movimentos de preço. Ele atualiza dinamicamente as previsões com base em novos dados de mercado, tornando-se valioso para estratégias adaptativas como reversão à média. Este artigo primeiro apresenta o filtro de Kalman, abordando seu cálculo e implementação. Em seguida, aplicamos o filtro a uma estratégia clássica de reversão à média no forex como exemplo. Por fim, realizamos diversas análises estatísticas comparando o filtro com uma média móvel em diferentes pares de forex.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Automatizando Estratégias de Trading em MQL5 (Parte 10): Desenvolvendo a Estratégia Trend Flat Momentum Automatizando Estratégias de Trading em MQL5 (Parte 10): Desenvolvendo a Estratégia Trend Flat Momentum
Neste artigo, desenvolvemos um Expert Advisor em MQL5 para a estratégia Trend Flat Momentum. Combinamos um cruzamento de duas médias móveis com filtros de momentum RSI e CCI para gerar sinais de negociação. Também abordamos backtesting e possíveis melhorias para desempenho em condições reais de mercado.