
Previsão de taxas de câmbio usando métodos clássicos de aprendizado de máquina: Modelos Logit e Probit
Introdução
Pesquisadores dos mercados financeiros sempre enfrentarão o desafio de escolher um modelo matemático para prever o comportamento futuro dos instrumentos de negociação. Hoje em dia, há uma enorme variedade desses modelos desenvolvidos. Por isso, surge a dúvida: como não se perder nesse mar de métodos e abordagens? Em quais modelos é melhor concentrar os esforços? Especialmente se você está apenas começando a prever com modelos de aprendizado de máquina. Se tentarmos simplificar o problema da previsão para uma resposta direta à pergunta “o preço de fechamento amanhã será maior que o preço de fechamento de hoje?”, então modelos de classificação binária seriam uma escolha lógica. Entre os mais simples e amplamente utilizados estão a regressão logística e a regressão probit. Esses modelos pertencem à forma mais comum de aprendizado de máquina, conhecida como aprendizado supervisionado.
A tarefa do aprendizado supervisionado consiste em ensinar nosso modelo a mapear o conjunto de entradas {x} (preditores ou características) para o conjunto de saídas {y} (alvos ou rótulos). Neste trabalho, vamos prever apenas dois estados de mercado: alta ou queda do preço de um par de moedas. Consequentemente, teremos apenas duas classes de rótulos y ∊ {1, 0}. Como preditores, usaremos padrões de preços, mais precisamente os incrementos padronizados dos preços com certo atraso. Esses dados formarão nosso conjunto de treinamento {x, y}, com o qual avaliaremos os parâmetros de nossos modelos. O modelo de previsão baseado nos classificadores treinados foi implementado como o EA LogitExpert.
Regressão binária Logit e Probit
Vamos passar rapidamente pela parte teórica. Vale dizer que o modelo mais simples de escolha binária é o modelo linear de probabilidade, no qual a probabilidade de ocorrência do evento de sucesso P(yn=1|xn) é uma função linear das variáveis explicativas:
P(yn=1|xn) = w0*1 + w1x1 + w2x2 + … + wkxk
Infelizmente, esse modelo tem uma grande limitação: o valor previsto pode ser maior que um ou menor que zero, o que impede interpretar o valor previsto como uma probabilidade. Para resolver esse problema, foi proposto o uso de funções conhecidas de distribuição de probabilidade, nas quais seriam inseridos os valores da função linear.
O modelo Probit é baseado na distribuição normal padrão N(0,1):
P(yn=1|xn) = F(xnw)=μn
-
n – índice inferior que indica o número da observação (exemplo),
-
yn - rótulo da classe,
-
F( ) - função de distribuição da normal (função de ativação),
-
xn – vetor de características,
-
w – vetor de parâmetros do modelo,
-
xnw – logit ou pré-ativação (representa o produto escalar entre o vetor de características e o vetor de parâmetros)
xnw = w0*1 + w1x1 + w2x2 + … + wkxk
Por sua vez, o modelo Logit é baseado na distribuição logística de probabilidades:
P(yn=1|xn) = L(xnw) = exp(xnw)/(1 + exp(xnw)) = μn
As funções de distribuição logística e normal são bastante próximas, e no intervalo [-1,2;1,2] são praticamente idênticas. Por isso, os modelos Logit e Probit frequentemente produzem resultados semelhantes, se a probabilidade não está muito próxima de zero ou de um. Assim, ao substituir o vetor de características nessas equações, permitimos calcular as probabilidades dos rótulos das classes, e, consequentemente, a probabilidade da direção futura do movimento do preço.
Preparação dos dados
Antes de estimar os parâmetros do modelo, nosso trabalho é definir as características, padronizá-las e apresentá-las no formato adequado para a função que irá encontrar os parâmetros ótimos (no sentido de minimizar a função de perda). Essa tarefa é realizada pela função GetDataset:
-
InpCount_ - define a quantidade de exemplos para treinamento
-
lag_ - número de características analisadas (incrementos de preço com defasagem)
-
string X — par de moedas pelo qual os preditores são calculados
-
string y — par de moedas pelo qual os rótulos são calculados
-
int start — número da barra a partir da qual começamos a coletar exemplos para treinamento
Como exemplo ilustrativo, usaremos como características os incrementos de preço defasados do par de moedas. Por exemplo, se o argumento da função for definido como lag_ = 4, então as características serão x{return-4,return-3,return-2,return-1} e teremos exatamente (InpCount_ - lag_) exemplos para treinamento.
//+------------------------------------------------------------------+ //|Get data for analysis: features and corresponding labels | //+------------------------------------------------------------------+ bool GetDataset(int InpCount_,int lag_,int start,matrix &Input_X,vector & Target_y,string X,string y) { matrix rates; matrix target; target.CopyRates(y, PERIOD_CURRENT, COPY_RATES_OHLC, start+1, InpCount_); rates.CopyRates(X, PERIOD_CURRENT, COPY_RATES_OHLC, start+2, InpCount_-1); rates = rates.Transpose(); target = target.Transpose(); int Class_ []; ArrayResize(Class_,InpCount_); for(int i=0; i<InpCount_; i++) { if(target[i,3] > target[i,0]) Class_[i] = 1; else Class_[i] = 0; } vector label=vector::Zeros(InpCount_-lag_); for(int i=0; i<InpCount_-lag_; i++) { label[i] = Class_[i+lag_]; // class label } matrix returns=matrix::Zeros(InpCount_-lag_, lag_); for(int j=0; j<lag_; j++) { for(int i=0; i<InpCount_-lag_; i++) { returns[i,j] =rates[i+j,3] - rates[i+j,0] ; // Input Data } } vector cols_mean=returns.Mean(0); vector cols_std=returns.Std(0); mean_ = cols_mean[lag_-1]; std_ = cols_std[lag_-1]; for(int j=0; j<lag_; j++) { for(int i=0; i<InpCount_-lag_; i++) { returns[i,j] = (returns[i,j] - cols_mean[lag_-1])/cols_std[lag_-1]; } } Input_X = returns; Target_y = label; return true; }
Na saída, obtemos uma matriz de características Input_X e um vetor de rótulos Target_y. Após formar o conjunto de treinamento, passamos à estimativa dos parâmetros.
Estimativa dos parâmetros do modelo
Na maioria dos casos, os parâmetros são estimados usando o método da máxima verossimilhança. No caso binário, os modelos Logit e Probit assumem que a variável dependente y segue uma distribuição de Bernoulli. Sendo assim, a função log-verossimilhança será igual a:
-
yn – rótulo da classe,
-
μn - probabilidade prevista da classe, obtida por regressão Logit ou Probit,
-
N – número de exemplos de treinamento
Para estimar os parâmetros, precisamos encontrar o máximo dessa função, mas como é comum minimizar a função de perda no aprendizado de máquina — e todos os otimizadores geralmente são ajustados para minimizar funções objetivo —, então simplesmente adicionamos um sinal de menos à função de verossimilhança. O resultado é o chamado logaritmo da verossimilhança negativa (NLL). Vamos minimizar essa função de perda utilizando o método de otimização quasi-Newtoniano de segunda ordem L-BFGS implementado na biblioteca Alglib. Esse método numérico é, geralmente, o mais usado para encontrar os parâmetros em modelos Logit e Probit. Outro método de otimização bastante conhecido é o dos mínimos quadrados com reponderação iterativa (IRLS).
//+------------------------------------------------------------------+ //| Derived class from CNDimensional_Func | //+------------------------------------------------------------------+ class CNDimensional_Logit : public CNDimensional_Func { public: CNDimensional_Logit(void) {} ~CNDimensional_Logit(void) {} virtual void Func(CRowDouble &w,double &func,CObject &obj); }; //+------------------------------------------------------------------+ //| Objective Function: Logit Negative loglikelihood | //+------------------------------------------------------------------+ void CNDimensional_Logit::Func(CRowDouble &w,double &func,CObject &obj) { double LLF[],probit[],probitact[]; vector logitact; ArrayResize(LLF,Rows_); ArrayResize(probit,Rows_); vector params=vector::Zeros(Cols_); for(int i = 0; i<Cols_; i++) { params[i] = w[i]; // vector of parameters } vector logit=vector::Zeros(Rows_); logit = Input_X_gl.MatMul(params); for(int i=0; i <Rows_; i++) { probit[i] = logit[i]; } if(probit_) MathCumulativeDistributionNormal(probit,0,1,probitact); // Probit activation else logit.Activation(logitact,AF_SIGMOID); // Logit activation //--------------------to avoid NAN error when calculating logarithm ------------------------------------ if(probit_) { for(int i = 0; i<Rows_; i++) { if(probitact[i]==1) probitact[i]= 0.999; if(probitact[i]==0) probitact[i]= 0.001; } } else { for(int i = 0; i<Rows_; i++) { if(logitact[i]==1) logitact[i]= 0.999; if(logitact[i]==0) logitact[i]= 0.001; } } //------------------------------------------------------------------------------------------------- double L2_reg; if(L2_) L2_reg = 0.5 * params.Dot(params); // L2_regularization else L2_reg =0; //------------------ calculate loss function------------------------------------------------------------- if(probit_) { for(int i = 0; i<Rows_; i++) { LLF[i]=target_y_gl[i]*MathLog(probitact[i]) + (1-target_y_gl[i])*MathLog(1-probitact[i]) ; if(!MathIsValidNumber(LLF[i])) { break; } } } else { for(int i = 0; i<Rows_; i++) { LLF[i]=target_y_gl[i]*MathLog(logitact[i]) + (1-target_y_gl[i])*MathLog(1-logitact[i]); if(!MathIsValidNumber(LLF[i])) { break; } } } func = -MathSum(LLF) + L2_reg/(Rows_*C_); // Negative Loglikelihood + L2_regularization //------------------------------------------------------------------------------------------------------ func_ = func; }
No entanto, apenas calcular as estimativas dos parâmetros não é suficiente, sendo importante também obter os erros padrão dessas estimativas, para que seja possível avaliar a significância dos nossos preditores.
Por exemplo, a popular biblioteca de aprendizado de máquina scikit-learn, por algum motivo, não calcula essas informações para o modelo Logit. Eu implementei o cálculo dos erros padrão como para o modelo Logit tanto quanto para o Probit, e agora é possível verificar se determinados preditores exercem influência estatisticamente significativa sobre a previsão. Essa é uma das razões pelas quais prefiro escrever o código do modelo Logit diretamente em MQL, em vez de usar modelos prontos convertidos via ONNX a partir de bibliotecas populares de aprendizado de máquina. Outra razão é a necessidade de um modelo dinâmico, que possa reotimizar os parâmetros do classificador a cada barra ou com uma frequência definida.
Mas, voltando à nossa função de perda, é preciso dizer que ela requer algum ajuste. A questão é que nossos classificadores, assim como métodos avançados baseados em redes neurais, tendem a apresentar sobreajuste. Isso se manifesta em estimativas anormalmente grandes dos parâmetros, e para evitar esse comportamento indesejado, precisamos de um método que limite tais valores. Esse método é conhecido como regularização L2:
-
λ = 1/C , C = (0,1]
Aqui simplesmente somamos à função de perda existente o quadrado da norma do vetor de parâmetros multiplicado por um hiperparâmetro λ (lambda). Quanto maior o lambda, maior é a penalização para parâmetros com valores elevados, e mais intensa é a regularização.
A função responsável pela estimativa dos parâmetros do classificador é a FitLogitRegression:
- bool L2 = false — por padrão, a regularização L2 está desativada,
- double C=1.0 — hiperparâmetro que controla a intensidade da regularização, quanto menor o valor, mais restritos serão os valores dos parâmetros otimizados,
- bool probit = false — por padrão, está ativado o modelo Logit,
- double alpha — nível de significância alpha da estatística LR com distribuição qui-quadrado
Essa função recebe como argumento a matriz de características e adiciona a ela uma chamada variável condicional ou fictícia, que assume valor igual a um em todas as observações. Isso é necessário para que possamos estimar o parâmetro w0 (o viés) em nosso modelo. Além das estimativas dos parâmetros, essa função também calcula suas matrizes de covariância, utilizadas para o cálculo dos erros padrão.
//+------------------------------------------------------------------+ //| Finding the optimal parameters for the Logit or Probit model | //+------------------------------------------------------------------+ vector FitLogitRegression(matrix &input_X, vector &target_y,bool L2 = false, double C=1.0,bool probit = false,double alpha = 0.05) { L2_=L2; probit_ = probit; C_ = C; double w[],s[]; CObject obj; CNDimensional_Logit ffunc; CNDimensional_Rep frep; ulong Rows = input_X.Rows(); ulong Cols = input_X.Cols(); matrix One=matrix::Ones(int(Rows),int(Cols+1)); for(int i=0;i<int(Cols); i++) { One.Col(input_X.Col(i),i+1); // design matrix } input_X = One; Cols = input_X.Cols(); Rows_ = int(Rows); Cols_ = int(Cols); Input_X_gl = input_X; target_y_gl = target_y; ArrayResize(w,int(Cols)); ArrayResize(s,int(Cols)); //--- initialization ArrayInitialize(w,0.0); ArrayInitialize(s,1.0); //--- optimization stop conditions double epsg=0.000001; double epsf=0.000001; double epsx=0.000001; double diffstep=0.000001; int maxits=0; //------------------------------ CMinLBFGSStateShell state; CMinLBFGSReportShell rep; CAlglib::MinLBFGSCreateF(1,w,diffstep,state); CAlglib::MinLBFGSSetCond(state,epsg,epsf,epsx,maxits); CAlglib::MinLBFGSSetScale(state,s); CAlglib::MinLBFGSOptimize(state,ffunc,frep,0,obj); CAlglib::MinLBFGSResults(state,w,rep); Print("TerminationType ="," ",rep.GetTerminationType()); Print("IterationsCount ="," ",rep.GetIterationsCount()); vector parameters=vector::Zeros(Cols); for(int i = 0; i<int(Cols); i++) { parameters[i]= w[i]; } Print("Parameters = "," ",parameters); //-------Likelihood Ratio Test LR----------------------------------------- double S = target_y.Sum(); // number of "success" ulong All = target_y.Size(); // all data double L0 = S*MathLog(S/All) + (All-S)*MathLog((All-S)/All); // Log-likelihood for the trivial model // Print("L0 = ",L0); // Print("LLF = ",func_); double LR; LR = 2*(-func_ - L0); // Likelihood Ratio Test LR int err; double Chi2 = MathQuantileChiSquare(1-alpha,Cols-1,err); // If H0 true ---> Chi2Distribution(alpha,v) Print("LR ",LR," ","Chi2 = ",Chi2); //-------------------------------------------------------------------------------- //-------------- calculate if model significant or not if(LR > Chi2) ModelSignificant = true; else ModelSignificant = false; //---------------------------------------------------- //-------------Estimation of the covariance matrix of parameters for the Probit model------------ vector logit = input_X.MatMul(parameters); // vector activation; logit.Activation(activation,AF_SIGMOID); // Logit activation double probit_SE[],probitact[]; ArrayResize(probit_SE,Rows_); for(int i=0; i <Rows_; i++) { probit_SE[i] = logit[i]; } if(probit_) { ulong size_parameters = parameters.Size(); matrix CovProbit=matrix::Zeros(int(size_parameters),int(size_parameters)); int err; vector a_=vector::Zeros(Rows_); vector b=vector::Zeros(Rows_); vector c=vector::Zeros(Rows_); vector xt=vector::Zeros(int(size_parameters)); for(int i = 0; i<Rows_; i++) { a_[i] = MathPow((MathProbabilityDensityNormal(probit_SE[i],0,1,err)),2); b[i] = MathCumulativeDistributionNormal(probit_SE[i],0,1,err); c[i] = a_[i]/(b[i]*(1-b[i])); xt = input_X.Row(i); CovProbit = CovProbit + c[i]*xt.Outer(xt); } CovProbit = CovProbit.Inv(); vector SE; SE = CovProbit.Diag(0); SE = MathSqrt(SE); // standard errors of parameters Print("Probit_SE = ", SE); } else { //-------------Estimation of the covariance matrix of parameters for the Logit model------------ vector v = vector::Zeros(Rows_); for(int i = 0; i<Rows_; i++) { v[i] = activation[i]*(1-activation[i]); } matrix R,Hesse,X,a,CovLogit; R.Diag(v,0); X = input_X.Transpose(); a = X.MatMul(R); Hesse = a.MatMul(input_X); CovLogit = Hesse.Inv(); vector SE; SE = CovLogit.Diag(0); SE = MathSqrt(SE); // standard errors of parameters Print("Logit_SE = ", SE); //----------------------------------------------- } return parameters; }
Depois que os parâmetros são encontrados e suas matrizes de covariância são calculadas, podemos partir para a etapa de previsão.
Previsão
A função responsável por prever os rótulos das classes e, consequentemente, gerar os sinais de compra ou venda é a Trade_PredictedTarget. Ela recebe os parâmetros otimizados como entrada e retorna o rótulo previsto da classe. A partir daí, o EA LogitExpert define as regras para abertura de posições. Elas são bastante simples. Se recebermos um sinal de compra (signal = 1) — abrimos uma posição longa. Se uma posição longa já estiver aberta, mantemos a posição. Ao receber um sinal de venda, a posição longa é encerrada e uma posição curta é aberta imediatamente.
O próprio código do EA LogitExpert
//+------------------------------------------------------------------+ //| LogitExpert.mq5 | //| Eugene | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Eugene" #property link "https://www.mql5.com" #property version "1.00" #include <\LogitReg.mqh> #include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh> CTrade m_trade; CPositionInfo m_position; sinput string symbol_X = "EURUSD"; // Input symbol sinput string symbol_y = "EURUSD"; // Target symbol input bool _probit_ = false; // Probit model input int InpCount = 20; // Depth of history input int _lag_ = 4; // Number of features input bool _L2_ = false; // L2_regularization input double _C_ = 1; // C(0,1) inverse of regularization strength input double alpha_ = 0.05; // Significance level Alpha (0,1) input int reoptimize_step = 2; // Reoptimize step #define MAGIC_NUMBER 23092024 int prev_bars = 0; MqlTick ticks; double min_lot; vector params_; matrix _Input_X; vector _Target_y; static int count_ = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { m_trade.SetExpertMagicNumber(MAGIC_NUMBER); m_trade.SetTypeFillingBySymbol(Symbol()); m_trade.SetMarginMode(); min_lot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print(__FUNCTION__," Deinitialization reason code = ",reason); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(!isnewBar(PERIOD_CURRENT)) return; double step; step = count_ % reoptimize_step; //------------------------------------Train Dataset------------------------------------------------- int start = 0; if(step == 0) { GetDataset(InpCount,_lag_,start,_Input_X,_Target_y,symbol_X,symbol_y); params_ = FitLogitRegression(_Input_X,_Target_y,_L2_,_C_,_probit_,alpha_); } count_ = count_+1; //-------------------------------------------------------------------------------------------------- //--- Get trade signal int signal = Trade_PredictedTarget(params_,start,_lag_,InpCount,symbol_X); Comment("Trade signal: ",signal," ","ModelSignificant: ",ModelSignificant); //--------------------------------------------- //--- Open trades based on Signals SymbolInfoTick(Symbol(), ticks); if(signal==1) { if(!PosExists(POSITION_TYPE_BUY) && ModelSignificant) { m_trade.Buy(min_lot,Symbol(), ticks.ask); PosClose(POSITION_TYPE_SELL); } else { PosClose(POSITION_TYPE_SELL); } } else { if(!PosExists(POSITION_TYPE_SELL) && ModelSignificant) { m_trade.Sell(min_lot,Symbol(), ticks.bid); PosClose(POSITION_TYPE_BUY); } else { PosClose(POSITION_TYPE_BUY); } } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Function tracks the occurrence of a new bar event | //+------------------------------------------------------------------+ bool isnewBar(ENUM_TIMEFRAMES TF) { if(prev_bars == 0) prev_bars = Bars(Symbol(), TF); if(prev_bars != Bars(Symbol(), TF)) { prev_bars = Bars(Symbol(), TF); return true; } return false; } //+------------------------------------------------------------------+ //|Function determines whether there is an open buy or sell position | //+------------------------------------------------------------------+ bool PosExists(ENUM_POSITION_TYPE type) { for(int i=PositionsTotal()-1; i>=0; i--) if(m_position.SelectByIndex(i)) if(m_position.Symbol()==Symbol() && m_position.Magic() == MAGIC_NUMBER && m_position.PositionType()==type) return true; return false; } //+------------------------------------------------------------------+ //|The function closes a long or short trade | //+------------------------------------------------------------------+ void PosClose(ENUM_POSITION_TYPE type) { for(int i=PositionsTotal()-1; i>=0; i--) if(m_position.SelectByIndex(i)) if(m_position.Symbol()==Symbol() && m_position.Magic() == MAGIC_NUMBER && m_position.PositionType()==type) if(!m_trade.PositionClose(m_position.Ticket())) printf("Failed to close position %d Err=%s",m_position.Ticket(),m_trade.ResultRetcodeDescription()); }
O que diferencia esse EA de várias outras abordagens? Em primeiro lugar, ele permite a reotimização dos parâmetros do classificador a cada (reoptimize_step) barras. Em segundo lugar, ele não se limita a estimar os parâmetros do modelo, mas também considera os erros padrão dessas estimativas, algo que muitas vezes é negligenciado. Não basta apenas encontrar os parâmetros “ótimos” para o conjunto de dados; é necessário também verificar se esses parâmetros são realmente significativos e se o modelo como um todo é relevante. Afinal, se os parâmetros não forem significativos, faz mais sentido ignorar esse sinal de negociação, não é mesmo?
Por isso, este EA também inclui um procedimento para testar a significância da hipótese do modelo. Nesse teste, a hipótese nula afirma que todos os parâmetros do modelo são iguais a zero (H0:w1=0,w2=0,w3=0,..., wk=0), enquanto a hipótese alternativa H1 afirma que pelo menos alguns parâmetros são diferentes de zero e, portanto, o modelo é útil para previsão. Para testar essa hipótese, utiliza-se o critério de razão de verossimilhança (LR), que mede a diferença entre o modelo proposto e o modelo trivial:
LR = 2(LLF – LLF0)
- LLF – valor obtido do logaritmo da função de verossimilhança,
- LLF0 - log-verossimilhança sob a hipótese nula, ou seja, para o modelo trivial
p0 = ∑(yn =1)/N – frequência amostral de sucesso,
LLF0 = N(p0*Ln(p0) + (1- p0)*Ln(1 – p0))
Quanto maior for essa diferença, melhor o modelo completo em comparação ao modelo trivial. Sob a hipótese nula, a estatística LR segue uma distribuição qui-quadrado com v graus de liberdade (v igual ao número de preditores). Se o valor calculado da estatística LR cair na região crítica, ou seja, LR > X2крит (alpha; v=lag_), então a hipótese H0 é rejeitada e, consequentemente, o sinal de negociação não é ignorado e uma posição de trading é aberta.
Um dos possíveis cenários. GBPUSD, Diário
Hiperparâmetros
Vale destacar que, além da estimativa dos parâmetros dos próprios modelos classificadores, também lidamos com um conjunto considerável de hiperparâmetros:
- profundidade do histórico
- quantidade de preditores
- nível de significância alpha
- intervalo de reotimização
A escolha desses hiperparâmetros é feita no testador de estratégias do MetaTrader 5. Uma das abordagens que pode melhorar o desempenho do EA é criar uma função que relacione o parâmetro da profundidade do histórico com o estado atual do mercado, ou seja, torná-lo dinâmico, assim como fizemos com os parâmetros dos modelos Logit e Probit. Mas isso já é outra história. Como dica, veja meu artigo Critério de homogeneidade de Smirnov como indicador de não estacionariedade de série temporal, no qual abordo a construção de um indicador de desajuste.
Considerações finais
Neste artigo, analisamos modelos de regressão com saídas binárias, aprendemos a estimar os parâmetros desses modelos e também implementamos o EA LogitExpert para testar e ajustar esses classificadores. Sua principal característica é permitir o re-treinamento dos parâmetros do classificador em tempo real, com base nos dados mais recentes e relevantes.
Foi dada atenção especial à estimativa dos erros padrão dos parâmetros, para o que foi necessário calcular as matrizes de covariância tanto para o modelo Logit quanto para o Probit.
Com base no critério de razão de verossimilhança, foi feita uma verificação da significância do modelo do classificador como um todo. Esse teste estatístico funciona como um filtro para eliminar sinais de negociação estatisticamente não confiáveis.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/16029
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- 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
Todas as perguntas a sua majestade, o mercado forex e a hipótese do mercado eficiente.
O título é, portanto, enganoso.
Obrigado, artigo muito interessante.
Na minha opinião, você já pode tentar usar dados fundamentais em viagens de um dia. Não se trata de criticar o artigo, mas de uma forma de pensar. Eu me pergunto como os dados macroeconômicos podem ser adequadamente "misturados" com os dados de preços. O problema é sua rara mudança, por exemplo. Provavelmente, a macroeconomia também pode ser usada de alguma forma no pré-processamento de preços - transição de taxas de câmbio nominais para reais, por exemplo.
O título é, portanto, enganoso.
Por quê? Ele usa um modelo preditivo de classificação que faz previsões. Ele conta corretamente o que é colocado no modelo. O que há de errado, então? Que o modelo não consegue superar uma previsão ingênua? Eu não prometi isso).
"A impossibilidade de prever taxas de câmbio usando métodos clássicos..."
"A impossibilidade de prever as taxas de câmbio usando métodos clássicos..."