Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 03): Entropia de Shannon
1.0 Introdução
Claude Shannon em 1948 apresentou o seu artigo “A mathematical theory of communication” que tinha o novo ideal da entropia de informação. A entropia é um conceito da física. É uma medida da extensão em que as partículas dentro de um objeto estão ativas. Se considerarmos os 3 estados da água, ou seja, gelo, líquido e vapor, por exemplo; podemos ver que a energia cinética da partícula é maior no vapor e menor no gelo. Esse mesmo conceito é aplicado na matemática via probabilidade. Considere os três conjuntos a seguir.
Conjunto 1:
Conjunto 2:
Conjunto 3:
Se você adivinhasse qual desses conjuntos teria a maior entropia?
Se você escolheu o último, acertou, mas como validamos essa resposta? A maneira mais simples de responder a isso pode ser considerando o número de maneiras pelas quais você pode reorganizar cada conjunto como a estimativa de entropia, ignorando os trechos de cores semelhantes. Para o primeiro conjunto, há apenas uma maneira de 'reorganizá-lo', no entanto, quando olhamos para os conjuntos depois, claramente, o número de permutações em relação à cor aumenta significativamente, portanto, você pode argumentar que o último conjunto tem a maior entropia
Existe uma maneira mais precisa de determinar a entropia baseada em informações. Se você escolhesse arbitrariamente uma bola do primeiro conjunto, antes de pegar essa bola, o que você saberia sobre ela? Bem, como o conjunto contém apenas bolas azuis, você pode ter certeza de que será uma bola azul. Você poderia dizer, portanto, que tenho informações completas sobre o que vou selecionar no conjunto 1. Conforme consideramos os conjuntos 2 e 3, nossas informações tornam-se cada vez menos completas, respectivamente. Em matemática, portanto, a Entropia está inversamente relacionada à informação. Quanto maior a entropia, mais incógnitas.
Então, como derivamos uma fórmula para calcular a entropia? Se considerarmos os conjuntos mais uma vez, quanto mais variedade houver nos tipos de bola dentro de um conjunto, maior será a entropia. O fator mais sensível para medir a informação que temos sobre uma bola antes de sua seleção pode ser a probabilidade de selecionar cada bola em um conjunto. Portanto, se considerarmos cada conjunto na totalidade e encontrarmos a probabilidade de selecionar 8 bolas com todas as cores selecionadas com a frequência com que aparecem em seu conjunto, dado que a seleção de cada bola é um evento independente; então a probabilidade (e, portanto, 'informação conhecida') para cada conjunto é o produto das probabilidades individuais para cada bola.
Conjunto 1:
(1.0 x 1.0 x 1.0 x 1.0 x 1.0 x 1.0 x 1.0 x 1.0) = 1.0
Conjunto 2:
(0.625 x 0.625 x 0.625 x 0.625 x 0.625 x 0.375 x 0.375 x 0.375) ~ 0.0050
Conjunto 3:
(0.375 x 0.375 x 0.375 x 0.25 x 0.25 x 0.25 x 0.25 x 0.125) ~ 0.000025
Conjunto 1:
-(8 x 0.125 x log2(1.0)) = 0.0
Conjunto 2:
(-(0.625 x log2(0.625)) - (0.375 x log2(0.375))) ~ 0.9544
Conjunto 3:
(- (0.375 x log2(0.375)) - (2 x 0.25 x log2(0.25)) - (0.125 x log2(0.125))) ~ 1.906
2.0 Criação da classe
Para este artigo, nós usaremos a classe Decision Forest da biblioteca MQL5. Especificamente, nós vamos abstrair a ideia de florestas aleatórias ao explorar a eficácia do nosso sinal de entropia de Shannon. Este artigo não é sobre florestas aleatórias, mas sobre a entropia de Shannon.
Vamos revisitar a classe da floresta de decisão, pois ela é a base do modelo da floresta aleatória. É uma coisa boa, as florestas de decisão são bastante inatas. Todos nós já usamos uma árvore de decisão, conscientemente ou não, em algum momento e, portanto, o próprio conceito não é estranho.
Vejamos um exemplo para ilustrar melhor como ele funciona.
Supondo que nosso conjunto de dados consiste nas barras de preço indicadas acima. Temos três velas em queda e cinco velas ascendentes (considere os mercados de baixa e alta como nossas classes) e queremos separar as classes usando seus atributos. Os atributos são a direção do preço e a comparação do comprimento da cabeça à cauda, pois podem ser precursores de mudanças na direção. Então, como nós podemos fazer isso?
A direção do preço parece um atributo direto para separar, já que o branco representa velas de baixa e azul de alta. Então, nós podemos usar a pergunta: “O preço está caindo?” para separar a primeira junção. Uma junção em uma árvore como o ponto onde o ramo se divide em dois — atendendo aos critérios para escolher o ramo Sim e o ramo Não.
Todos os ramos Não (velas ascendentes) têm caudas mais longas do que as cabeças, então terminamos aqui, mas no ramo Sim, esse não é o caso, portanto, há mais trabalho a ser feito. Ao usar o segundo atributo, perguntamos: “As cabeças são mais longas que as caudas?” para fazer uma segunda divisão.
As duas velas em declínio com cabeças mais longas estão sob o sub-ramo Sim e uma vela em declínio com uma cauda mais longa é classificada no sub-ramo direito. Essa árvore de decisão foi capaz de usar os dois atributos para dividir os dados perfeitamente de acordo com os nossos critérios.
Em sistemas de negociação reais, o volume de ticks, a hora do dia e uma série de outros indicadores de negociação podem ser usados na construção de uma árvore de decisão mais abrangente. No entanto, a premissa central de cada pergunta em um nó é encontrar o único atributo que divide (ou classifica) os dados acima em dois conjuntos diferentes, com os membros criados de cada conjunto sendo semelhantes.
Mudando para a floresta aleatória, como o próprio nome indica, ela consiste em um grande número de árvores de decisão individuais que funcionam como um grupo. Para cada árvore na floresta aleatória, se faz uma predição de classe e a classe com mais votos é selecionada como predição do modelo. O principal conceito por trás da floresta aleatória é fácil de ignorar, mas é muito poderoso — a sabedoria das massas. Ao fazer previsões, as árvores não correlacionadas superam as árvores individuais, independentemente da quantidade de dados em que os indivíduos são treinados. Baixa correlação é o ponto crucial. A razão para isso é que as árvores protegem umas às outras de seus erros individuais de acordo com Ton (desde que não errem todas constantemente na mesma direção). Para que as florestas aleatórias funcionem, o sinal de previsão precisa ser melhor que a média e as perdas/erros de cada árvore precisam ter uma correlação baixa entre si.
Por exemplo, se você tivesse dois sistemas de negociação, onde no primeiro você pode colocar até mil ordens de margem de um dólar em um ano e o outro você só pode colocar uma ordem de margem de 1000 dólares qual você prefere assumindo expectativa semelhante? Para a maioria das pessoas, é o primeiro sistema, pois dá ao trader "mais controle".
Então, de que maneira os algoritmos de floresta aleatória garantem que a característica de cada árvore individual não esteja muito correlacionada com a característica de qualquer uma das outras árvores em uma floresta? Isso pode se resumir a dois recursos:
2.0.1 Bagging
As árvores de decisão são muito sensíveis aos dados de treinamento com pequenas alterações que podem resultar em florestas significativamente diferentes. As florestas aleatórias usam isso fazendo com que cada árvore faça uma amostra aleatória do conjunto de dados durante a substituição, resultando em árvores diferentes. Esse processo é conhecido como bagging, uma abreviação de bootstrap aggregation.
Observe que com o bagging não estamos substituindo os dados de treinamento em conjuntos menores, mas em vez dos dados de treinamento originais, pegamos uma amostra aleatória de tamanho N com algumas substituições. O tamanho de ajuste inicial N é mantido. Por exemplo, se nossos dados de treinamento forem [U, V, W, X, Y, Z], podemos fornecer a uma de nossas árvores a seguinte lista [U, U, V, X, X, Z]. Em ambas as listas o tamanho N (seis) é mantido e que “U” e “X” são ambos repetidos nos dados selecionados aleatoriamente.
2.0.2 Aleatoriedade de Características
Normalmente, em uma árvore de decisão, dividir o nó envolve considerar todas as características possíveis e selecionar uma que produza a distinção mais ampla entre as observações no eventual nó esquerdo versus aquelas no eventual nó direito. Por outro lado, em uma floresta aleatória, cada árvore pode escolher apenas um subconjunto aleatório de características. Isso tende a forçar mais variação dentro da floresta e, em última análise, leva a uma correlação mais baixa entre as árvores e a uma maior diversificação.
Vejamos um exemplo visual — na figura acima, a árvore de decisão tradicional (em azul) pode selecionar entre todos as quatro características ao decidir como dividir o nó. Ele decide usar a Característica 1 (preto e sublinhado), pois divide os dados em grupos tão separados quanto possível.
Agora vamos dar uma olhada em nossa floresta aleatória. Examinaremos apenas duas das árvores da floresta neste exemplo. Quando verificamos a Árvore 1 da floresta aleatória, descobrimos que ela só pode considerar as características 2 e 3 (selecionados aleatoriamente) para sua decisão de divisão de nós. Sabemos por nossa árvore de decisão tradicional (em azul) que a Característica 1 é o melhor recurso para divisão, mas a Árvore 1 não pode ver a Característica 1, então é forçado a ir com a Característica 2 (preto e sublinhado). A Árvore 2, por outro lado, só pode ver as Característica 1 e 3, portanto, pode escolher a Característica 1.
Portanto, em florestas aleatórias, árvores que não são apenas treinadas em conjuntos de dados (graças ao bagging), mas também usam várias características na tomada de decisões.
Ao usar florestas aleatórias, nosso expert processa os resultados das negociações anteriores e os sinais de mercado para chegar a uma decisão de compra ou venda. Ao obter um sinal e não apenas um rastreador da entropia, nós consideraremos separadamente a entropia das barras de preços positivas e negativas dentro de um conjunto recente.
A construção e o treinamento de florestas aleatórias ocorrerão apenas na otimização com uma única thread.
2.1 Classe Signal do Expert
// wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Signals of'Shannon Entropy' | //| Type=SignalAdvanced | //| Name=Shannon Entropy | //| ShortName=SE | //| Class=CSignalSE | //| Page=signal_se | //| Parameter=Reset,bool,false,Reset Training | //| Parameter=Trees,int,50,Trees number | //| Parameter=Regularization,double,0.15,Regularization Threshold | //| Parameter=Trainings,int,21,Trainings number | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CSignalSE. | //| Purpose: Class of generator of trade signals based on | //| the 'Shannon Entropy' signals. | //| Is derived from the CExpertSignal class. | //+------------------------------------------------------------------+ class CSignalSE : public CExpertSignal { public: //Decision Forest objects. CDecisionForest DF; //Decision Forest CMatrixDouble DF_SIGNAL; //Decision Forest Matrix for inputs and output CDFReport DF_REPORT; //Decision Forest Report for results int DF_INFO; //Decision Forest feedback double m_out_calculations[2], m_in_calculations[__INPUTS]; //Decision Forest calculation arrays //--- adjusted parameters bool m_reset; int m_trees; double m_regularization; int m_trainings; //--- methods of setting adjustable parameters void Reset(bool value){ m_reset=value; } void Trees(int value){ m_trees=value; } void Regularization(double value){ m_regularization=value; } void Trainings(int value){ m_trainings=value; } //Decision Forest FUZZY system objects CMamdaniFuzzySystem *m_fuzzy; CFuzzyVariable *m_in_variables[__INPUTS]; CFuzzyVariable *m_out_variable; CDictionary_Obj_Double *m_in_text[__INPUTS]; CDictionary_Obj_Double *m_out_text; CMamdaniFuzzyRule *m_rule[__RULES]; CList *m_in_list; double m_signals[][__INPUTS]; CNormalMembershipFunction *m_update; datetime m_last_time; double m_last_signal; double m_last_condition; CSignalSE(void); ~CSignalSE(void); //--- method of verification of settings virtual bool ValidationSettings(void); //--- method of creating the indicator and timeseries virtual bool InitIndicators(CIndicators *indicators); //--- methods of checking if the market models are formed virtual int LongCondition(void); virtual int ShortCondition(void); bool m_random; bool m_read_forest; int m_samples; //--- method of initialization of the oscillator bool InitSE(CIndicators *indicators); double Data(int Index){ return(Close(StartIndex()+Index)-Close(StartIndex()+Index+1)); } void ReadForest(); void WriteForest(); void SignalUpdate(double Signal); void ResultUpdate(double Result); double Signal(void); double Result(void); bool IsNewBar(void); };
2.1.1 Sinais
Essa entropia será ponderada pelo índice de atualidade.
if(_data>0.0) { _long_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); } else if(_data<0.0) { _short_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); }
E também será ponderado pela magnitude das barras de preço.
if(_data>0.0) { _long_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); } else if(_data<0.0) { _short_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); }
O sinal gerado será a entropia das barras negativas menos a entropia das barras positivas. O raciocínio aqui é se a entropia das barras negativas exceder a das barras positivas, então temos menos informações sobre as barras negativas e, portanto, uma posição vendida do que para as barras positivas, que também implicam uma posição comprada. Na superfície, parece que este será um sistema de tendência 'perigoso'! No entanto, devido aos pesos anexados acima no cálculo da entropia, esse pode não ser o caso.
Os sinais serão atualizados quando a condição de compra ou venda exceder o limite de abertura, pois isso implica que uma posição será aberta. Isso acontecerá no timer, então modificaremos o expert montado pelo assistente para atender a isso.
//+------------------------------------------------------------------+ //| "Timer" event handler function | //+------------------------------------------------------------------+ void OnTimer() { if(PositionSelect(Symbol()) && Signal_ThresholdClose<=fabs(filter0.m_last_condition)) { filter0.ResultUpdate(filter0.Result()); } // if(!PositionSelect(Symbol()) && Signal_ThresholdOpen<=fabs(filter0.m_last_condition)) { filter0.SignalUpdate(filter0.m_last_signal); } ExtExpert.OnTimer(); }
A função de origem na classe só será executada na otimização conforme já mencionado.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSignalSE::SignalUpdate(double Signal) { if(MQLInfoInteger(MQL_OPTIMIZATION)) { m_samples++; DF_SIGNAL.Resize(m_samples,__INPUTS+2); for(int i=0;i<__INPUTS;i++) { DF_SIGNAL[m_samples-1].Set(i,m_signals[0][i]); } // DF_SIGNAL[m_samples-1].Set(__INPUTS,Signal); DF_SIGNAL[m_samples-1].Set(__INPUTS+1,1-Signal); } }
2.1.2 Resultados
Os resultados serão baseados no lucro da última posição fechada.
if(HistorySelect(0,m_symbol.Time())) { int _deals=HistoryDealsTotal(); for(int d=_deals-1;d>=0;d--) { ulong _deal_ticket=HistoryDealGetTicket(d); if(HistoryDealSelect(_deal_ticket)) { if(HistoryDealGetInteger(_deal_ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT) { _result=HistoryDealGetDouble(_deal_ticket,DEAL_PROFIT); break; } } } } return(_result);
E eles serão atualizados quando a condição de compra ou venda exceder o limite de fechamento, pois isso implica que uma posição será fechada. Isso também acontecerá no cronômetro com a função de classe apenas atualizando os arquivos da floresta de decisão ao otimizar.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSignalSE::ResultUpdate(double Result) { if(MQLInfoInteger(MQL_OPTIMIZATION)) { int _err; if(Result<0.0) { double _odds = MathRandomUniform(0,1,_err); // DF_SIGNAL[m_samples-1].Set(__INPUTS,_odds); DF_SIGNAL[m_samples-1].Set(__INPUTS+1,1-_odds); } } }
2.1.3 Escrita da Floresta
A floresta será gravada no tick antes de uma leitura, portanto, modificamos o expert montado pelo assistente para acomodar isso.
//+------------------------------------------------------------------+ //| "Tick" event handler function | //+------------------------------------------------------------------+ void OnTick() { if(!signal_se.m_read_forest) signal_se.WriteForest(); ExtExpert.OnTick(); }
2.1.4 Leitura da Floresta
A floresta será lida no testador no final de uma passagem do testador, então modificamos o expert montado pelo assistente para acomodar isso.
//+------------------------------------------------------------------+ //| "Tester" event handler function | //+------------------------------------------------------------------+ double OnTester() { signal_se.ReadForest(); return(0.0); }
2.2 Classe Money do Expert
Para este artigo, também estamos explorando a criação de uma classe de dimensionamento de posição personalizada para ser usada com o assistente. Pegaremos a classe 'Money Size Optimized' e a modificaremos para normalizar os tamanhos das posições com base na entropia de Shannon. Nossa nova interface ficará assim:
// wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Trading with 'Shannon Entropy' optimized trade volume | //| Type=Money | //| Name=SE | //| Class=CMoneySE | //| Page=money_se | //| Parameter=ScaleFactor,int,3,Scale factor | //| Parameter=Percent,double,10.0,Percent | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CMoneySE. | //| Purpose: Class of money management with 'Shannon Entropy' optimized volume. | //| Derives from class CExpertMoney. | //+------------------------------------------------------------------+ class CMoneySE : public CExpertMoney { protected: int m_scale_factor; public: double m_absolute_condition; CMoneySE(void); ~CMoneySE(void); //--- void ScaleFactor(int scale_factor) { m_scale_factor=scale_factor; } void AbsoluteCondition(double absolute_condition) { m_absolute_condition=absolute_condition; } virtual bool ValidationSettings(void); //--- virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); protected: double Optimize(double lots); };
A variável 'm_absolute_condition' será o valor absoluto do(s) inteiro(s) retornado(s) pelas funções 'LongCondition' e 'ShortCondition'. Como ele é um valor normalizado, nós podemos usar o seu tamanho para dimensionar o nosso tamanho da posição. Essa variável será passada da classe signal para a classe money por meio das modificações no expert montado pelo assistente.
//+------------------------------------------------------------------+ //| Global expert object | //+------------------------------------------------------------------+ CExpert ExtExpert; CSignalSE *signal_se; CMoneySE *money_se;
E na função tick
//+------------------------------------------------------------------+ //| "Tick" event handler function | //+------------------------------------------------------------------+ void OnTick() { if(!signal_se.m_read_forest) signal_se.WriteForest(); money_se.AbsoluteCondition(fabs(signal_se.m_last_condition)); ExtExpert.OnTick(); }
E as principais modificações estarão na função 'Optimize' abaixo:
//+------------------------------------------------------------------+ //| Optimizing lot size for open. | //+------------------------------------------------------------------+ double CMoneySE::Optimize(double lots) { double lot=lots; //--- normalize lot size based on magnitude of condition lot*=(20*m_scale_factor/fmax(20.0,((100.0-m_absolute_condition)/100.0)*20.0*m_scale_factor*m_scale_factor)); //--- reduce lot based on number of losses orders without a break if(m_scale_factor>0) { //--- select history for access HistorySelect(0,TimeCurrent()); //--- int orders=HistoryDealsTotal(); // total history deals int losses=0; // number of consequent losing orders CDealInfo deal; //--- for(int i=orders-1;i>=0;i--) { deal.Ticket(HistoryDealGetTicket(i)); if(deal.Ticket()==0) { Print("CMoneySE::Optimize: HistoryDealGetTicket failed, no trade history"); break; } //--- check symbol if(deal.Symbol()!=m_symbol.Name()) continue; //--- check profit double profit=deal.Profit(); if(profit>0.0) break; if(profit<0.0) losses++; } //--- if(losses>1){ lot*=m_scale_factor; lot/=(losses+m_scale_factor); lot=NormalizeDouble(lot,2);} } //--- normalize and check limits double stepvol=m_symbol.LotsStep(); lot=stepvol*NormalizeDouble(lot/stepvol,0); //--- double minvol=m_symbol.LotsMin(); if(lot<minvol){ lot=minvol; } //--- double maxvol=m_symbol.LotsMax(); if(lot>maxvol){ lot=maxvol; } //--- return(lot); }
3.0 Assistente MQL5
Nós vamos desenvolver dois expert advisors, um com apenas a classe signal que criamos, mais a negociação de volume mínimo para gestão do dinheiro, e o segundo com as classes signal e gestão de dinheiro que criamos.
4.0 Testador de Estratégia
A otimização em execução para o primeiro expert fornece um fator de lucro de 2.89 e um índice de Sharpe de 4.87 versus um fator de lucro de 3.65 e índice de Sharpe de 5.79 da otimização do segundo expert.
Primeiro relatório
Primeira curva de patrimônio
Segundo relatório
Segunda curva de patrimônio
5.0 Conclusão
Os experts anexados foram otimizados nos preços de abertura do período de 4 horas para os níveis ideais de take profit e stop loss. Isso significa que você não pode replicar esses resultados em uma conta real ou mesmo no testador de estratégia em todos os modos de tick. Não era esse o objetivo do artigo. Em vez de tentar criar um graal de que todos devem replicar essas séries de artigos, procure expor ideias distintas que podem ser personalizadas ainda mais para que cada trader possa apresentar sua vantagem. Os mercados em geral ainda estão fortemente correlacionados, e é por isso que procurar uma vantagem pode ser útil em uma alta volatilidade. Obrigado por ler!
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/11487
- 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