English Русский 中文 Español Deutsch 日本語
preview
Teoria das Categorias em MQL5 (Parte 8): Monoides

Teoria das Categorias em MQL5 (Parte 8): Monoides

MetaTrader 5Testador | 23 agosto 2023, 09:47
282 0
Stephen Njuki
Stephen Njuki

Introdução

Em nosso artigo anterior, sobre teoria das categorias, desvendamos os conceitos-chave de conjuntos múltiplos, conjuntos relativos e conjuntos indexados e exploramos sua importância na negociação algorítmica. Agora, neste seguimento, apresentamos o conceito de monoides. Monoides formam uma base essencial em matemática e ciência da computação, fornecendo uma abordagem estruturada para modelar operações em conjuntos de elementos.

Por definição, monoides são uma coleção de 3 coisas, nomeadamente um conjunto; uma operação binária que pega qualquer dois elementos desse conjunto e sempre retorna um elemento que também é membro desse conjunto; e um elemento de identidade que pertence ao conjunto, de modo que, quando pareado com qualquer outro membro desse conjunto na operação binária mencionada anteriormente, o resultado é sempre o outro elemento com o qual esse elemento de identidade é pareado. Esta operação binária também é associativa. Dito de outra forma, um monoide é uma maneira de combinar elementos em um conjunto, aderindo a regras predefinidas. Monoides fornecem uma abordagem sistemática e flexível para agregar e manipular dados.

Formalmente, um monoide M, com elementos membros a, b e c; um elemento de identidade e; e operação binária *; pode ser definido como:

M * M - - > M;                      1


e * a - - > a;                        2


a * e - - > a;                        3


a * (b * c) - - > (a * b) * c     4

Então, a equação 1 enfatiza que o pareamento de quaisquer 2 membros do conjunto retorna um membro do conjunto. As equações 2 e 3 destacam a importância do elemento de identidade, pois o resultado é sempre o elemento na operação binária que não é a identidade. E finalmente, a equação 4 destaca a associatividade da operação binária *.


Ilustração e métodos

Para ilustrar a possível aplicação de monoides para traders, consideraremos 5 decisões com as quais alguns ou a maioria dos traders pode se deparar antes de executar negociações. Estas são:

  1. A duração do período de análise retrospectiva.
  2. O período de tempo do gráfico usado.
  3. Preço aplicável.
  4. Parâmetro aplicável.
  5. E se, com essas informações, você deve negociar em uma faixa ou tendência.

Para cada uma dessas decisões, iremos elaborar:

  • um conjunto de possíveis valores dos quais escolher;
  • uma operação binária que ajuda a selecionar entre quaisquer dois elementos. Esta operação pode ser um método MQL5 que é chamado por outro método iterativamente através de todos os elementos do conjunto até que uma seleção seja feita.
  • E um índice para o elemento de identidade deste conjunto. Índice porque este elemento estará em um conjunto, que é um array.
  • A escolha da operação binária ideal que simplesmente escolhe entre 2 elementos será uma das seguintes:
  • A operação que escolhe o menor dos dois elementos.
  • A operação que escolhe o maior dos dois elementos em avaliação.
  • A operação que escolhe do conjunto, o elemento mais próximo da média dos dois elementos na operação binária.
  • E, finalmente, a operação que escolhe do conjunto o elemento que está mais distante da média dos dois elementos na operação binária.

Estamos considerando 5 pontos de decisão: período de retrospectiva, intervalo de tempo, preço aplicado, indicador e interpretação de sinal. Outro trader pode ter diferentes pontos-chave de decisão. Portanto, é importante ter em mente que isso não é um guia definitivo passo a passo, mas algo simplesmente escolhido para este artigo.

Ao usar monoides da teoria das categorias para classificar dados, existem algumas precauções que devem ser tomadas para garantir resultados precisos e significativos. Aqui está uma lista possível a ser considerada:

1) Tenha uma Estrutura de Monoide bem definida:

Você precisa garantir que os dados com os quais está trabalhando formem uma estrutura de monoide válida conforme a definição. Isso significa verificar se ela satisfaz os axiomas do monoide, como ter um elemento de identidade e ser associativa sob a operação binária. Aqui estão três exemplos de armadilhas que podem levar a uma estrutura de monoide mal definida:

Falta de Fechamento:

Se a operação binária usada no monoide não resultar em elementos que pertencem ao mesmo conjunto ou domínio, o fechamento é violado. Por exemplo, se você tentar definir um monoide em números naturais usando a operação de subtração, encontrará elementos que não são números naturais (por exemplo, subtrair 5 de 3 resulta em -2, que não é um número natural). Estritamente falando, uma operação não é nem uma função de adição, subtração, multiplicação. É simplesmente um método com regras bem definidas que pega quaisquer dois elementos do conjunto e retorna um elemento que é membro desse conjunto.

Não Associatividade:

Outra armadilha é quando a operação binária não satisfaz a propriedade associativa. Se os elementos em seu monoide não se combinarem de maneira associativa, isso pode levar a resultados ambíguos e inconsistentes. Por exemplo, considere um monoide onde a operação é multiplicação e os elementos são matrizes. Se você tiver três matrizes a, b e c, então esta operação não é associativa, ou seja, (a * b) * c ≠ a * (b * c), comprometendo assim a estrutura do monoide.

Falta de Elemento de Identidade:

Todo monoide deve ter um elemento de identidade, que atua como o elemento neutro sob a operação binária. Se o monoide não possui um elemento de identidade, torna-se problemático realizar operações com certos elementos. Por exemplo, se você define um monoide em números reais usando a operação de divisão, não há elemento de identidade, já que a divisão por zero é indefinida.

Por outro lado, aqui estão três exemplos de estruturas de monoide adequadas:

Adição de Inteiros:

O conjunto de inteiros, equipado com a operação binária de adição (+), forma um monoide. O elemento de identidade é 0, e a adição é associativa e fechada no conjunto de inteiros.

Multiplicação de Números Racionais Não-zero:

O conjunto de números racionais não-zero (frações), com a operação binária de multiplicação (×), forma um monoide. O elemento de identidade é 1, e a multiplicação é associativa e fechada para números racionais não-zero.

Concatenação de Strings:

O conjunto de strings, com a operação binária de concatenação, forma um monoide. O elemento de identidade é a string vazia (""), e a concatenação é associativa e fechada para strings.

Esses exemplos demonstram estruturas de monoide bem definidas que satisfazem as propriedades necessárias e podem ser usadas efetivamente para fins de classificação.

Semântica e Interpretabilidade:

Compreenda a semântica da operação do monoide e sua relação com seus dados. Considere se as classificações resultantes estão alinhadas com as interpretações pretendidas e fazem sentido no contexto do domínio do seu problema. Estes 5 exemplos tentam ilustrar isso:

Classificação de Frequência de Palavras:

Suponha que você esteja usando monoides para classificar orientações trimestrais de empresas a partir de transcrições de chamadas com base na frequência de palavras. Enquanto a operação de monoide poderia simplesmente envolver a soma das contagens de palavras, a interpretabilidade das classificações resultantes precisaria ser considerada cuidadosamente, pois depende da semântica atribuída a diferentes faixas de frequência. Por exemplo, você pode interpretar documentos com altas frequências de palavras como sendo mais focados em um tópico específico, enquanto baixas frequências de palavras específicas podem indicar conteúdo mais amplo ou diversificado. O que você não quer fazer é focar apenas na contagem total de palavras e usá-la como base em suas principais operações de monoide.

Análise de Sentimento:

Vamos dizer que você está usando um monoide para classificar o sentimento de um texto. A operação de monoide poderia ter um desempenho melhor agregando pontuações de sentimento de palavras ou frases individuais. Vamos considerar um exemplo. Suponha que você tenha um conjunto de avaliações de clientes para um produto e deseja classificá-las em três categorias de sentimento: positivo, neutro e negativo. Você decide usar uma abordagem de monoide em que a operação do monoide envolve agregar pontuações de sentimento de frases individuais dentro de cada avaliação. Neste exemplo, você atribui pontuações de sentimento variando de -1 a 1, onde -1 representa um sentimento altamente negativo, 0 representa sentimento neutro e 1 representa sentimento altamente positivo. A operação do monoide, então, faria uma simples soma das pontuações de sentimento. Agora, vamos considerar uma avaliação de cliente:

Avaliação: "O produto é bom. No entanto, o atendimento ao cliente foi abaixo do esperado."

A maneira adequada de classificar isso seria dividir em frases individuais e atribuir pontuações de sentimento a cada frase:

Frase 1: "O produto é bom." - Pontuação de Sentimento: 0.8 (positivo)

Frase 2: "No entanto, o atendimento ao cliente foi abaixo do esperado." - Pontuação de Sentimento: -0.7 (negativo)

Para obter a pontuação de sentimento geral, para a avaliação, aplicamos a operação do monoide, que neste caso é a soma:

Pontuação de Sentimento Geral = 0.8 + (-0.7) = 0.1

Com base na semântica atribuída aos intervalos de pontuação de sentimento, você interpreta a pontuação de sentimento geral de 0.1 como um sentimento levemente positivo. Portanto, você classifica esta avaliação como "neutra" ou "levemente positiva" com base na classificação do monoide. O que você não quer fazer é considerar ambas as frases como uma única e atribuir uma pontuação anedótica porque a palavra "bom" está presente. Seria uma boa prática considerar os detalhes.

Classificação de Imagens:

Um monoide para classificar imagens com base em características visuais, como cor, textura ou forma, envolveria combinar essas características, e a interpretabilidade dessas características combinadas, que resultam em classificações, dependerá de como você as mapeia em suas categorias pretendidas ou significativas. A semântica atribuída a diferentes combinações de características pode influenciar grandemente como você entende os resultados da classificação.

Considere esta ilustração para mostrar a importância da semântica e interpretabilidade na classificação de imagens usando monoides. Suponha que você esteja usando uma abordagem de monoide para classificar imagens em duas categorias: "Cão" e "Gato" (para traders, isso poderia ser substituído, por exemplo, por padrões de cabeça e ombro de alta ou de baixa, mas o princípio permanece o mesmo) com base em características visuais. A operação de monoide envolveria combinar características de cor e textura lidas das imagens. Para nossos propósitos, suponha que você tenha duas características visuais principais: "Cor do Pelo" e "Complexidade da Textura". A cor do pelo pode ser classificada como "Clara" ou "Escura", enquanto a complexidade da textura pode ser classificada como "Simples" ou "Complexa". Agora, vamos considerar duas imagens.

Deixe a Imagem 1 ter um gato branco com textura de pelo simples, ou seja:

  • Cor da pelagem: Clara
  • Complexidade da textura: Simples
  • Seja a imagem 2 um cachorro preto com uma textura de pelo complexa, o que implica:
  • Cor da pelagem: Escura
  • Complexidade da textura: Complexa

Para classificar essas imagens usando a abordagem de monoide, você combina as características visuais de acordo com a operação de monoide (por exemplo, concatenação, soma, etc.):

Para a Imagem 1: "Clara" + "Simples" = "ClaraSimples"

Para a Imagem 2: "Escura" + "Complexa" = "EscuraComplexa"

Agora, vem a parte crucial, que é a semântica e interpretabilidade. Você precisa atribuir significado às características combinadas para mapeá-las de volta para categorias significativas. No nosso caso, como estamos usando um exemplo excessivamente simples:

"ClaraSimples" poderia ser interpretado como uma categoria "Gato" porque a cor de pelo clara e a textura simples são características comuns de gatos.

"EscuraComplexa" poderia ser interpretado como uma categoria "Cão", já que a cor do pelo escuro e a textura complexa são frequentemente associadas a cães.

Ao atribuir semânticas e interpretações apropriadas às características combinadas, você pode classificar a Imagem 1 como "Gato" e a Imagem 2 como "Cão" corretamente.

Segmentação de Clientes:

Suponha que você esteja usando monoides para segmentar clientes com base em seu comportamento de compra. A operação de monoide pode envolver a agregação de dados de transação ou atributos do cliente. No entanto, a interpretabilidade dos segmentos resultantes depende de como você interpreta e rotula esses segmentos. Por exemplo, você pode atribuir rótulos como "clientes de alto valor" ou "clientes propensos a churn" com base na semântica e no conhecimento de domínio.

Classificação de Séries Temporais:

Considere usar monoides para classificar dados de séries temporais, como tendências do mercado de ações. A operação de monoide pode envolver a combinação de várias características como preço, volume e volatilidade. No entanto, a interpretabilidade das classificações depende de como você define a semântica das combinações resultantes em relação às condições de mercado. Diferentes interpretações podem levar a percepções distintas e implicações na tomada de decisões.

Em todos esses exemplos, a semântica atribuída à operação de monoide e às classificações resultantes é crucial para uma interpretação significativa e tomada de decisão. A consideração cuidadosa dessas semânticas garante que as classificações resultantes estejam alinhadas com as interpretações desejadas e permitam uma análise e entendimento eficazes dos dados.

3) Pré-processamento de Dados:

Isso é importante para fins de controle de qualidade dos dados antes de serem classificados com monoides. Seria apropriado pré-processar os dados para garantir compatibilidade com a estrutura de monoide. Por exemplo, todas as saídas de funções operacionais devem ser membros definitivos do conjunto monoide e não dados flutuantes com múltiplos pontos decimais que, ao serem arredondados, possam ser ambíguos. Para alcançar isso, pode-se normalizar os dados (regularização) ou transformá-los em um formato adequado para a operação de monoide. Além disso, a manipulação de valores ausentes precisa ser abordada para melhor consistência geral de seu sistema de negociação.

4) Homogeneidade dos Dados:

Certifique-se de que os dados que você está classificando possuam um certo grau de homogeneidade dentro de cada categoria. Por exemplo, na etapa de seleção de indicadores, o monoide definido que usaremos deve ter ambos os indicadores com valores ou ponderações consistentes e comparáveis. Considerando que estamos usando o oscilador RSI e as Bollinger Bands, claramente isso não ocorre por padrão. No entanto, normalizaremos um deles para garantir que ambos sejam comparáveis e homogêneos. Monoides funcionam melhor quando aplicados a dados que exibem características semelhantes dentro de cada classe.

Cardinalidade das Categorias:

Considere a cardinalidade ou o número de categorias distintas que podem ser formadas usando o monoide. Se o número de categorias resultantes for muito alto ou muito baixo, isso pode afetar a utilidade da classificação ou a interpretabilidade dos resultados.

Vamos ilustrar o impacto da cardinalidade de dados das categorias com um exemplo:

Suponha que você esteja trabalhando em uma tarefa de classificação para prever a direção de um par de moedas forex com base no sentimento de eventos de notícias no calendário, e você tem um conjunto de dados com uma variável alvo "Sentimento" que pode assumir três valores possíveis: "acima da expectativa", "atendeu à expectativa" e "abaixo da expectativa".

Aqui está um exemplo de conjunto de dados:

Indicador Sentimento
Produção industrial do Fed m/m abaixo
Índice de preços do GDT atende
Estoques m/m acima
Índice do mercado imobiliário da NAHB abaixo

Neste exemplo, você pode observar que a variável "Sentimento" tem três categorias: "Acima", "Correspondeu" e "Abaixo". A cardinalidade refere-se ao número de categorias distintas em uma variável.

A cardinalidade da variável "Sentimento" neste conjunto de dados é 3 porque tem três categorias únicas.

A cardinalidade de dados das categorias pode ter implicações para tarefas de classificação. Vamos considerar dois cenários:

Cenário 1:

Aqui temos uma Cardinalidade de Dados Desbalanceada levando a um conjunto de dados desequilibrado em que a categoria de sentimento "Acima" tem um número significativamente maior de amostras em comparação com as categorias "Correspondeu" e "Abaixo". Por exemplo, vamos supor que 80% das amostras são rotuladas como "Acima", enquanto 10% são "Correspondeu" e 10% são "Abaixo".

Neste cenário, a cardinalidade de dados desbalanceada pode levar a vieses no modelo de classificação. O modelo pode tornar-se enviesado para prever a classe majoritária ("Acima") com mais frequência, enquanto enfrenta dificuldades para prever com precisão as classes minoritárias ("Correspondeu" e "Abaixo"). Isso pode resultar em menor precisão e revocação para as classes minoritárias, impactando o desempenho geral e a interpretabilidade do modelo de classificação.

Cenário 2:

Neste caso, em vez de atribuir valores de string aos elementos do nosso conjunto monoide, como foi o caso acima, usamos dados de ponto flutuante para ponderar e descrever cada elemento no conjunto monoide com mais precisão. Isso também implica que temos cardinalidade ilimitada, ou seja, não há um número definido de possíveis pesos/valores para cada um dos elementos do conjunto, como fizemos no cenário 1.

6) Escalabilidade:

Você precisaria avaliar a escalabilidade da classificação monoide, especialmente ao lidar com grandes conjuntos de dados. Dependendo da complexidade computacional, pode ser necessário considerar técnicas ou otimizações alternativas para lidar eficientemente com grandes volumes de dados. Uma abordagem ao lidar com grandes conjuntos de dados é realizar a engenharia de caraterísticas. Os homomorfismos monoides podem ser usados na engenharia de características para uma variedade de tarefas. Um exemplo disso poderia ser a avaliação de empresas públicas.

Os homomorfismos poderiam transformar as características de entrada em um novo espaço de recurso com maior poder preditivo para modelos de avaliação. Vamos considerar um exemplo, se você tem um conjunto de dados contendo várias métricas financeiras, como receita, lucros, ativos e passivos para um conjunto de empresas públicas.

Uma abordagem comum é usar homomorfismos monoides para derivar e focar em índices financeiros-chave que são amplamente utilizados em modelos de avaliação. Por exemplo, você pode definir um homomorfismo que mapeia o conjunto monoide de receita para um novo conjunto monoide normalizado representando a taxa de crescimento da receita. Essa transformação reduzirá claramente suas necessidades de dados, tornando seus monoides mais escaláveis, porque muitas empresas listadas independentemente no conjunto monoide de receita compartilharão os mesmos valores de taxa de crescimento de receita no codomínio.

Da mesma forma, você pode usar um homomorfismo monoide para mapear o monoide de lucros para um monoide representando o lucro por ação (LPA). O LPA é uma métrica de avaliação amplamente utilizada que indica a lucratividade de uma empresa em uma base por ação. E existem muitos outros índices cruciais que podem ser úteis, todos visando o mesmo objetivo de manter seu modelo de classificação monoide escalável.

Por outro lado, você quer minimizar a dependência de frameworks de computação distribuída, como Apache Hadoop ou Apache Spark, para processar os dados em paralelo em vários nós de um cluster. Essas abordagens permitem distribuir a carga de trabalho e acelerar o tempo de processamento, tornando possível lidar com grandes conjuntos de dados, no entanto, eles acarretarão em custos significativos downstream. ‘Downstream’ porque todos os problemas que eles estão tentando resolver poderiam ter sido tratados de forma mais tática no nível de design do monoide em primeiro lugar.

7) Generalização:

Você teria que avaliar a generalização dos resultados da classificação usando dados novos e não vistos. Os métodos de classificação baseados em monoide devem fornecer uma categorização confiável e consistente em diferentes conjuntos de dados e contextos.

Suponhamos que você esteja desenvolvendo uma classificação baseada em monoide para prever a solvabilidade de candidatos a empréstimos. Seu conjunto de dados conteria dados históricos de empréstimos, como renda, pontuação de crédito, relação dívida/renda e histórico de emprego, e assim por diante.

Para avaliar a generalização dos resultados finais de classificação, você teria que avaliar o quão bem o modelo se desempenha em dados novos e não vistos. Por exemplo, após treinar o modelo em um conjunto de dados de uma região ou período de tempo específico, você o testaria em um conjunto de dados separado de uma região ou período de tempo diferente. Se o modelo mostrar desempenho de categorização consistente e confiável em diferentes conjuntos de dados, isso indicaria uma boa generalização.

Para alcançar a generalização em métodos de classificação baseados em monoide, as possíveis armadilhas incluem overfitting, viés de dados e viés de seleção de características. Para explicar, overfitting ocorre quando a função de operação do monoide (por exemplo) torna-se muito específica para os dados de treinamento, resultando em um desempenho ruim em novos dados. Por outro lado, o viés de dados pode acontecer se o conjunto de treinamento não for representativo da população em geral, levando assim a uma classificação tendenciosa. Com o viés de seleção de características, a escolha de características que não capturam a informação relevante para o contexto dado, afetará a generalização do modelo.

8) Métricas de Avaliação:

Defina métricas de avaliação apropriadas para avaliar a qualidade e eficácia da classificação do monoide. Essas métricas devem estar alinhadas com o seu problema e objetivos específicos, levando em consideração fatores como precisão, acurácia, revocação ou F1-score.

9) Overfitting e Underfitting:

Proteja-se contra overfitting ou underfitting do modelo de classificação monoide. Aplique técnicas como validação cruzada, regularização ou parada precoce para prevenir esses problemas e promover a generalização do modelo.

10) Interpretabilidade e Explicabilidade:

Considere a interpretabilidade e explicabilidade das classificações resultantes. Monoides podem fornecer capacidades poderosas de classificação, mas é importante entender como as decisões de classificação são feitas e poder explicá-las de uma maneira significativa.


Implementação:

Período de Retrospectiva:

Um domínio monoide de 8 inteiros numerados de 1 a 8 será usado para representar as opções de períodos de retrospectiva disponíveis. Uma unidade de período será equivalente a 4 e nosso período de teste será fixado em uma hora, embora nossos períodos de análise variarão conforme abordado na próxima seção. Em cada nova barra, precisaremos selecionar um período. Nossa unidade de medida para cada um será o tamanho percentual relativo do movimento naquele período quando comparado a um período anterior de igual duração. Portanto, por exemplo, se a mudança de preço em pontos ao longo do período 1 (4 barras) foi A, e a do período anterior a ele foi B, então a ponderação ou valor do período 1 seria dada pela fórmula a seguir

= ABS(A)/(ABS(A) + ABS(B))

Onde a função ABS() representa o valor absoluto. Esta fórmula é verificada para divisão por zero garantindo que o mínimo do denominador seja pelo menos um ponto no tamanho do ativo sendo considerado.

Nossa operação monoide e elemento de identidade serão selecionados a partir da otimização dos métodos apresentados no início do artigo.

Período de tempo:

O conjunto monoide para o prazo terá 8 períodos de tempo, a saber:

  • PERÍODO_H1
  • PERÍODO_H2
  • PERIOD_H3,
  • PERIOD_H4,
  • PERIOD_H6,
  • PERIOD_H8,
  • PERIOD_H12,
  • PERIOD_D1

A ponderação e atribuição de valor de cada um desses prazos seguirão o mesmo padrão acima no período de retrospectiva, ou seja, compararemos as mudanças percentuais no preço de fechamento com base nas mudanças de preço da barra anterior no respectivo prazo.

Preço Aplicado:

O conjunto monoide de preço aplicado terá 4 preços aplicados possíveis para escolher:

  • MODE_MEDIAN ((High + Low) / 2)
  • MODE_TYPICAL ((High + Low + Close) / 3)
  • MODE_OPEN
  • MODE_CLOSE

A ponderação e atribuição de valor aqui variarão do que temos acima. Neste caso, usaremos o desvio padrão de cada preço aplicado durante o período selecionado no período de retrospectiva para determinar a ponderação ou valor de cada preço aplicado. A seleção do índice de operação e identidade será a mesma que acima.

Indicador:

O conjunto monoide para seleção de indicador terá apenas duas escolhas, a saber:

  • Oscilador RSI
  • Bandas de Bollinger

O oscilador RSI é normalizado para a faixa de 0 a 100, mas o indicador Bollinger Bands não é apenas não normalizado, ele apresenta múltiplos fluxos de buffer. Para normalizar as Bollinger Bands e torná-las comparáveis ao oscilador RSI, pegaremos a diferença entre o preço atual e a banda base C, e a dividiremos pelo tamanho do espaço entre as bandas superiores e as bandas inferiores, D. Portanto, nosso valor para as bandas primeiro parecerá assim:

= C/(ABS(C) + ABS(D))

Como antes, esse valor será verificado quanto a divisões por zero. Embora este valor possa ser negativo e tenda ao valor decimal 1.0. Para normalizar esses dois aspectos e tê-lo na faixa de 0 – 100 como o RSI, somamos 1.0 para garantir que ele seja sempre positivo e, em seguida, multiplicamos a soma por 50.0 para garantir que ele esteja na faixa de 0 – 100. Assim, nossos valores que agora variam de 0 – 100 para ambos, RSI e Bollinger Bands, representarão nossa ponderação e a função operador e o índice do elemento serão selecionados conforme mencionado nos métodos acima.

Solução:

Para este último e final monoide, nosso conjunto também terá apenas duas escolhas. Estas são:

  • Negociação de tendências
  • Negociação em faixa

Para quantificar esses dois elementos, consideraremos, durante o período de retrospectiva selecionado, a quantidade de pontos de preço que um ativo recua contra a sua tendência eventual no final do período, como uma porcentagem da faixa de preço total deste período. Isso certamente será um valor decimal de 0.0 – 1.0. Ele medirá diretamente o peso da negociação com a faixa, significando que a negociação com a tendência será este valor subtraído de 1.0. O método de operação e seleção do índice é como acima.

É assim que implementaríamos nossas decisões monoide como uma instância da classe de trailing incorporada do EA.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_8(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<8;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(O==OP_LEAST)
      {
         for(int i=0;i<8;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_MOST)
      {
         for(int i=0;i<8;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_CLOSEST)
      {
         for(int i=0;i<8;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<8;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
      else if(O==OP_FURTHEST)
      {
         for(int i=0;i<8;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<8;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_4(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<4;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            /*printf(__FUNCSIG__+
               " values size: "+IntegerToString(ArraySize(Values))+
               " in indices size: "+IntegerToString(ArraySize(InputIndices))+
               " in indices index: "+IntegerToString(InputIndices[i])
               );*/
               
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(O==OP_LEAST)
      {
         for(int i=0;i<4;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_MOST)
      {
         for(int i=0;i<4;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[i/2]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[i/2]=i+1; }
            else { OutputIndices[i/2]=m_lookback.Identity(); }
         }
      }
      else if(O==OP_CLOSEST)
      {
         for(int i=0;i<4;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<4;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
      else if(O==OP_FURTHEST)
      {
         for(int i=0;i<4;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<4;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[i/2]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingCT::Operate_2(CMonoid<double> &M,EOperations &O,double &Values[],int &InputIndices[],int &OutputIndices[])
   {
      for(int i=0;i<2;i++)
      {
         m_element.Let();
         if(m_lookback.Get(i,m_element))
         {
            if(!m_element.Get(0,Values[InputIndices[i]]))
            {
               printf(__FUNCSIG__+" Failed to get double for 1 at: "+IntegerToString(i+1));
            }
         }
         else{ printf(__FUNCSIG__+" Failed to get element for 1 at: "+IntegerToString(i+1)); }
      }
      
      //
      
      if(m_lookback_operation==OP_LEAST)
      {
         for(int i=0;i<2;i+=2)
         {
            if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[0]=i; }
            else if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[0]=i+1; }
            else { OutputIndices[0]=m_lookback.Identity(); }
         }
      }
      else if(m_lookback_operation==OP_MOST)
      {
         for(int i=0;i<2;i+=2)
         {
            if(Values[InputIndices[i]]>Values[InputIndices[i+1]]){ OutputIndices[0]=i; }
            else if(Values[InputIndices[i]]<Values[InputIndices[i+1]]){ OutputIndices[0]=i+1; }
            else { OutputIndices[0]=m_lookback.Identity(); }
         }
      }
      else if(m_lookback_operation==OP_CLOSEST)
      {
         for(int i=0;i<2;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=DBL_MAX;
            for(int ii=0;ii<2;ii++)
            {
               if(_gap>fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[0]=_index;
         }
      }
      else if(m_lookback_operation==OP_FURTHEST)
      {
         for(int i=0;i<2;i+=2)
         {
            int _index=-1;
            double _mean=0.5*(Values[InputIndices[i]]+Values[InputIndices[i+1]]),_gap=0.0;
            for(int ii=0;ii<2;ii++)
            {
               if(_gap<fabs(_mean-Values[InputIndices[ii]])){ _gap=fabs(_mean-Values[InputIndices[ii]]); _index=ii;}
            }
            //
            if(_index==-1){ _index=m_lookback.Identity(); }
            
            OutputIndices[0]=_index;
         }
      }
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CTrailingCT::GetLookback()
   {
      m_close.Refresh(-1);
      
      int _x=StartIndex();
      
      for(int i=0;i<8;i++)
      {
         int _period=(__LOOKBACKS[i]*PeriodSeconds(PERIOD_H4))/PeriodSeconds(m_period);
         double _value=fabs(m_close.GetData(_x)-m_close.GetData(_x+_period))/(fabs(m_close.GetData(_x)-m_close.GetData(_x+_period))+fabs(m_close.GetData(_x+_period)-m_close.GetData(_x+_period+_period)));
         
         m_element.Let();
         m_element.Cardinality(1);
         if(m_element.Set(0,_value))
         {
            ResetLastError();
            if(!m_lookback.Set(i,m_element,true))
            {
               printf(__FUNCSIG__+" Failed to assign element at index: "+IntegerToString(i)+", for lookback. ERR: "+IntegerToString(GetLastError()));
            }
         }
      }
      
      //r of 8
      double _v1[8];ArrayInitialize(_v1,0.0);
      int _i1_in[8];for(int i=0;i<8;i++){ _i1_in[i]=i; }
      int _i1_out[4];ArrayInitialize(_i1_out,-1);
      Operate_8(m_lookback,m_lookback_operation,_v1,_i1_in,_i1_out);
      
      
      //r of 4
      double _v2[8];ArrayInitialize(_v2,0.0);
      int _i2_out[2];ArrayInitialize(_i2_out,-1);
      Operate_4(m_lookback,m_lookback_operation,_v2,_i1_out,_i2_out);
      
      
      //r of 2
      double _v3[8];ArrayInitialize(_v3,0.0);
      int _i3_out[1];ArrayInitialize(_i3_out,-1);
      Operate_2(m_lookback,m_lookback_operation,_v2,_i2_out,_i3_out);
      
      return(4*__LOOKBACKS[_i3_out[0]]);
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CTrailingCT::GetTimeframe(void)
   {
      for(int i=0;i<8;i++)
      {
         ResetLastError();
         double _value=0.0;
         double _buffer[];ArrayResize(_buffer,3);ArrayInitialize(_buffer,0.0);ArraySetAsSeries(_buffer,true);
         if(CopyClose(m_symbol.Name(),__TIMEFRAMES[i],0,3,_buffer)>=3)
         {
            _value=fabs(_buffer[0]-_buffer[1])/(fabs(_buffer[0]-_buffer[1])+fabs(_buffer[1]-_buffer[2]));
         }
         else{ printf(__FUNCSIG__+" Failed to copy: "+EnumToString(__TIMEFRAMES[i])+" close prices. err: "+IntegerToString(GetLastError())); }
         
         m_element.Let();
         m_element.Cardinality(1);
         if(m_element.Set(0,_value))
         {
            ResetLastError();
            if(!m_timeframe.Set(i,m_element,true))
            {
               printf(__FUNCSIG__+" Failed to assign element at index: "+IntegerToString(i)+", for lookback. ERR: "+IntegerToString(GetLastError()));
            }
         }
      }
      
      //r of 8
      double _v1[8];ArrayInitialize(_v1,0.0);
      int _i1_in[8];for(int i=0;i<8;i++){ _i1_in[i]=i; }
      int _i1_out[4];ArrayInitialize(_i1_out,-1);
      Operate_8(m_timeframe,m_timeframe_operation,_v1,_i1_in,_i1_out);
      
      
      //r of 4
      double _v2[8];ArrayInitialize(_v2,0.0);
      int _i2_out[2];ArrayInitialize(_i2_out,-1);
      Operate_4(m_timeframe,m_timeframe_operation,_v2,_i1_out,_i2_out);
      
      
      //r of 2
      double _v3[8];ArrayInitialize(_v3,0.0);
      int _i3_out[1];ArrayInitialize(_i3_out,-1);
      Operate_2(m_timeframe,m_timeframe_operation,_v2,_i2_out,_i3_out);
      
      return(__TIMEFRAMES[_i3_out[0]]);
   }


Portanto, a função 'Operate_8' agrupa 8 elementos no conjunto monoide e apresenta uma seleção de 4, um de cada par. Da mesma forma, a função 'Operate_4' agrupa os 4 elementos obtidos de 'Operate_8' e apresenta uma seleção de 2, novamente um de cada par e, finalmente, a função 'Operate_2' agrupa esses dois elementos de 'Operate_4' para apresentar o elemento vencedor.


Se executarmos testes com esse sistema para determinar a parada de perda ideal para posições abertas, como parte de um EA que usa o sinal RSI embutido da classe de sinal de especialista e a gestão de dinheiro fixo da classe de dinheiro de especialista, obtemos o relatório abaixo.

r_1

Como controle, uma execução similar em um EA muito parecido, cuja única diferença do nosso é o sistema de rastreamento, que é a parada de perda móvel embutida baseada em média móvel, fornece o relatório abaixo.

r_2


Considerações finais

Analisamos os monoides como um meio de classificação de dados e, portanto, um bloco de decisão. A atenção foi voltada para a importância de ter monoides bem formados que são generalizáveis e não ajustados excessivamente, bem como outras precauções que podem ser chave para realizar um sistema bem equilibrado. Também analisamos uma possível implementação desse sistema que ajusta posições de stop loss trabalhando como uma instância da classe de trailing incorporada do EA.


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/12634

Arquivos anexados |
ct_8.mqh (64.34 KB)
TrailingCT8.mqh (35.04 KB)
Redes neurais de maneira fácil (Parte 39): Go-Explore - uma abordagem diferente para exploração Redes neurais de maneira fácil (Parte 39): Go-Explore - uma abordagem diferente para exploração
Continuamos com o tema da exploração do ambiente no aprendizado por reforço. Neste artigo, abordaremos mais um algoritmo, o Go-Explore, que permite explorar eficazmente o ambiente durante a fase de treinamento do modelo.
Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 6): transformada de Fourier Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 6): transformada de Fourier
A transformada de Fourier é um método de decompor uma onda de pontos de dados em possíveis partes constituintes que foi introduzida por Joseph Fourier. Esse recurso pode ser útil para os traders, e é isso que abordaremos neste artigo.
Algoritmo de recompra: Simulação de negociação em várias moedas Algoritmo de recompra: Simulação de negociação em várias moedas
Neste artigo, criaremos um modelo matemático para simular a precificação em várias moedas e concluiremos o estudo, que comecei no artigo anterior, sobre o princípio de diversificação como parte da busca por mecanismos para aumentar a eficiência da negociação.
Implementando um algoritmo de treinamento ARIMA em MQL5 Implementando um algoritmo de treinamento ARIMA em MQL5
Neste artigo, implementaremos um algoritmo que aplica o modelo integrado de autorregressão com média móvel (modelo Box-Jenkins) usando o método de minimização de função de Powell. Box e Jenkins afirmaram que a maioria das séries temporais pode ser modelada usando uma ou ambas das duas estruturas.