Discussão do artigo "R quadrado como uma estimativa da qualidade da curva de saldo da estratégia" - página 2

 
Li na diagonal, mas, mesmo assim, tirei conclusões importantes para mim mesmo. Muito obrigado ao autor.
 
Obrigado pelo artigo útil! Repostou-o! :-)
 

Figura 19: Distribuição da correlação LR para 10.000 passeios aleatórios


Figura 20: Distribuição de R^2 para 10.000 passeios aleatórios

Não vejo como o R^2 pode assumir os valores negativos mostrados no segundo gráfico? Sim, e há dúvidas com relação ao primeiro gráfico. Se a regressão linear for plotada corretamente, parece que o RQ de Pearson (LR) não deveria ser negativo. Mas no gráfico ele não é. Onde estou errando?


Entendi. Não estou errado em lugar nenhum, apenas os gráficos têm R^2 e LR personalizados - a multiplicação por -1 do valor real ocorre se o último elemento da série numérica for menor que o primeiro. Seria bom escrever sobre isso antes dos gráficos.

 

No documento, a regressão linear é considerada com um erro - por meio de CLinReg::LRLine.

Prova

#include <Graphics\Graphic.mqh> 
#include <Math\Stat\Normal.mqh>
#include <Math\Alglib\Alglib.mqh>

// Retorna os valores Y da linha (y(x)=a*x+b)
void GetLine( const double a, const double b, const int Amount, double &Result[] )
{
  ArrayResize(Result, Amount);
  
  for (int i = 0; i < Amount; i++)
    Result[i] = a * i + b;    
}

// Retorna uma regressão linear por meio de CLinReg::LRLine
void GetLinearRegression( const double &Array[], double &Result[] )
{
  const int Total = ArraySize(Array);
  
  CMatrixDouble XY(Total, 2);
  
  for (int i = 0; i < Total; i++)
  {
    XY[i].Set(0, i);
    XY[i].Set(1, Array[i]);
  }
  
  int retcode;
  double a, b;
  
  CLinReg::LRLine(XY, Total, retcode, a, b);

  GetLine(a, b, Total, Result);    
}

// Retorna uma regressão linear via CAlglib::LRBuild + CAlglib::LRUnpack
void GetLinearRegression2( const double &Array[], double &Result[] )
{
  const int Total = ArraySize(Array);
  
  CMatrixDouble XY(Total, 2);
  
  for (int i = 0; i < Total; i++)
  {
    XY[i].Set(0, i);
    XY[i].Set(1, Array[i]);
  }
  
  int retcode;
  
  CLinearModelShell lm;
  CLRReportShell    ar;
//--- matrizes para armazenar resultados de regressão
  double lr_coeff[];
//--- cálculo dos coeficientes de regressão linear
  CAlglib::LRBuild(XY, Total, 1, retcode, lm, ar);
//--- obtenção de coeficientes de regressão linear
  CAlglib::LRUnpack(lm, lr_coeff, retcode);

  GetLine(lr_coeff[0], lr_coeff[1], Total, Result);      
}

void ToChart( const double &Array1[], const double &Array2[], const int X = 0, const int Y = 0, const int Width = 780, const int Height = 380 )
{
  static const string Name = __FILE__;
  
  CGraphic Graphic; 

  if (ObjectFind(0, Name) < 0) 
    Graphic.Create(0, Name, 0, X, Y, Width, Height); 
  else 
    Graphic.Attach(0, Name); 

  Graphic.CurveAdd(Array1, CURVE_LINES);
  Graphic.CurveAdd(Array2, CURVE_LINES);
  
  Graphic.CurvePlotAll(); 
  Graphic.Update();  
}

void GetRandomArray( double &Array[], const int Amount = 1 e3 )
{
  double Random[];
  
  MathSrand(GetTickCount()); 

  MathRandomNormal(0, 1, Amount, Random); 
  MathCumulativeSum(Random, Array);
}

#define  TOSTRING(A) #A + " = " + (string)(A) + "\n"

void OnStart() 
{   
  double Array[];
  
  GetRandomArray(Array);  
  
  double Estimate[];
  double Estimate2[];
     
  GetLinearRegression(Array, Estimate);
  GetLinearRegression2(Array, Estimate2);

  const double R = CAlglib::PearsonCorr2(Array, Estimate);
  const double R2 = CAlglib::PearsonCorr2(Array, Estimate2);
  
  Print(TOSTRING(R) +
        TOSTRING((Array[0] > Array[ArraySize(Array) - 1]) ? -R : R) +
        TOSTRING(R2));
  
  ToChart(Array, Estimate2);
}


Resultado

R = -0.5864718581193301
(Array[0]>Array[ArraySize(Array)-1])?-R:R = -0.5864718581193301
R2 = 0.58647185811933


O sinal está incorreto. A implementação alternativa de LR (CAlglib::LRBuild + CAlglib::LRUnpack) conta corretamente:


 
fxsaber:

Os gráficos das distribuições de correlação LR e R^2 para os 10.000 exemplos independentes apresentados no artigo mostram que R^2 != LR^2.

Não entendo por que o segundo grau da distribuição "côncava" original a torna "plana"?

Foi aqui que me provaram que eu estava errado. Para mim, a afirmação não é nada óbvia

O que é surpreendente é que, por meio de uma simples ação matemática (elevando ao segundo grau), removemos completamente os efeitos marginais indesejados da distribuição.

Por isso, decidi confirmar experimentalmente por meio de animação (não acredite na minha palavra)

#include <Graphics\Graphic.mqh> 
#include <Math\Stat\Normal.mqh>
#include <Math\Alglib\Alglib.mqh>

// Retorna os valores Y da linha (y(x)=a*x+b)
void GetLine( const double a, const double b, const int Amount, double &Result[] )
{
  ArrayResize(Result, Amount);
  
  for (int i = 0; i < Amount; i++)
    Result[i] = a * i + b;    
}

// Retorna uma regressão linear via CAlglib::LRBuild + CAlglib::LRUnpack
void GetLinearRegression( const double &Array[], double &Result[] )
{
  const int Total = ArraySize(Array);
  
  CMatrixDouble XY(Total, 2);
  
  for (int i = 0; i < Total; i++)
  {
    XY[i].Set(0, i);
    XY[i].Set(1, Array[i]);
  }
  
  int retcode;
  
  CLinearModelShell lm;
  CLRReportShell    ar;
//--- matrizes para armazenar resultados de regressão
  double lr_coeff[];
//--- cálculo dos coeficientes de regressão linear
  CAlglib::LRBuild(XY, Total, 1, retcode, lm, ar);
//--- obtenção de coeficientes de regressão linear
  CAlglib::LRUnpack(lm, lr_coeff, retcode);

  GetLine(lr_coeff[0], lr_coeff[1], Total, Result);      
}

// Calcula R
double GetCustomR( const double &Array[] )
{
  double Estimate[];
   
  GetLinearRegression(Array, Estimate);
   
  const double R = CAlglib::PearsonCorr2(Array, Estimate);

  return((Array[0] > Array[ArraySize(Array) - 1]) ? -R : R);
}

// Calcula os vetores aleatórios R
void GetRandomCustomR( const int Amount, const int VectorSize, double &Result[] )
{
  double Random[];
  double Sum[];
  
  MathSrand(GetTickCount()); 

  ArrayResize(Result, Amount);
  
  for (int i = 0; i < Amount; i++)
  {
    MathRandomNormal(0, 1, VectorSize, Random); 
    MathCumulativeSum(Random, Sum);
    
    Result[i] = GetCustomR(Sum);
  }  
}

void ToChart( const double &X[],  const double &Y[], const string Str = NULL, const int X0 = 0, const int Y0 = 0, const int Width = 780, const int Height = 380 )
{
  static const string Name = __FILE__;
  
  CGraphic Graphic; 

  if (ObjectFind(0, Name)<0) 
    Graphic.Create(0, Name, 0, X0, Y0, Width, Height); 
  else 
    Graphic.Attach(0, Name); 

  Graphic.BackgroundMain(Str); 
  Graphic.BackgroundMainSize(16); 

  Graphic.CurveAdd(X, Y, CURVE_HISTOGRAM).HistogramWidth(6);
  
  Graphic.CurvePlotAll(); 
  Graphic.Update();  
}

void MathPow( double &Result[], const double &Array[], const double Pow )
{
  const int Size = ArrayResize(Result, ArraySize(Array));
  
  for (int i = 0; i < Size; i++)
    Result[i] = (Array[i] < 0) ? -MathPow(-Array[i], Pow) : MathPow(Array[i], Pow);
}

// https://www.mql5.com/pt/docs/standardlibrary/mathematics/stat/normal
//+------------------------------------------------------------------+ 
//| Calcular frequências para o conjunto de dados| 
//+------------------------------------------------------------------+ 
bool CalculateHistogramArray(const double &data[],double &intervals[],double &frequency[], 
                             double &maxv,double &minv,const int cells=10) 
  { 
   if(cells<=1) return (false); 
   int size=ArraySize(data); 
   if(size<cells*10) return (false); 
   minv=data[ArrayMinimum(data)]; 
   maxv=data[ArrayMaximum(data)]; 
   double range=maxv-minv; 
   double width=range/cells; 
   if(width==0) return false; 
   ArrayResize(intervals,cells); 
   ArrayResize(frequency,cells); 
//--- definir os centros dos intervalos 
   for(int i=0; i<cells; i++) 
     { 
      intervals[i]=minv+(i+0.5)*width; 
      frequency[i]=0; 
     } 
//--- preencher as frequências de intervalo 
   for(int i=0; i<size; i++) 
     { 
      int ind=int((data[i]-minv)/width); 
      if(ind>=cells) ind=cells-1; 
      frequency[ind]++; 
     } 
   return (true); 
  } 

void DistributionToChart( const double &Array[], const string Str = NULL, const int NCells = 51 )
{
  double X[];          // centros de intervalo do histograma 
  double Y[];          // número de valores da amostra que se enquadram no intervalo 
  double Max, Min;     // valores máximos e mínimos na amostra 
  
  CalculateHistogramArray(Array, X, Y, Max, Min, NCells);   

  ToChart(X, Y, Str);
}

void OnInit() 
{   
  double R[];
  
  GetRandomCustomR(1 e3, 1 e4, R);
  
  double Array[];

  const int Max = 50;
  
  while (!IsStopped())
    for (int i = 1; !IsStopped() && i < (Max << 1); i++)
    {
      const double Pow = (i > Max) ? ((Max << 1) - i) * 0.1 : i * 0.1;
      
      MathPow(Array, R, Pow);
      DistributionToChart(Array, "Distribution of R^" + DoubleToString(Pow, 1));      
      
      Sleep(100);
    }
}



Parece ser assim.

 

 

Fig. 21: Valor R^2 como um critério de otimização personalizado

Onde está oMQL LR Correlation, que aparece na figura? Ou esse e muitos outros parâmetros são calculados apenas para execuções únicas e, portanto, estão ausentes em ENUM_STATISTICS?

Em caso afirmativo, sugerimos calcular esse parâmetro a partir das considerações razoáveis mencionadas neste artigo: por patrimônio líquido sem MM e ao quadrado.


ZY Eu medi quanto tempo leva para calcular GetCustomR para uma matriz de um milhão de valores (como patrimônio líquido) - 2,5 segundos. Isso é muito tempo. Tudo é gasto no cálculo do LR(CAlglib::LRBuild + CAlglib::LRUnpack). Mas, às vezes, o LR curvo por meio de CLinReg::LRLine é uma ordem de magnitude mais rápida. Se você o fizer, ele se tornará tolerável nas otimizações como um critério de otimização.

Документация по MQL5: Стандартные константы, перечисления и структуры / Состояние окружения / Статистика тестирования
Документация по MQL5: Стандартные константы, перечисления и структуры / Состояние окружения / Статистика тестирования
  • www.mql5.com
Максимальная просадка баланса в процентах. В процессе торговли баланс может испытать множество просадок, для каждой фиксируется относительное значение просадки в процентах. Возвращается наибольшее значение Максимальная...
 
Dennis Kirichenko:

Ah! Eu sempre pensei que fosse 100. Obrigado, artigo interessante.

Sim, esse é um número que encontrei em livros conceituados sobre R e estatística. Mas não consegui encontrar o link, desculpe.

Dennis Kirichenko:

Também é comum realizar testes de significância no coeficiente de regressão. Até mesmo o Alglib os tem :-)

Obviamente, os testes são para distribuição normal. Temos uma distribuição uniforme.

PearsonCorrelationSignificance(), SpearmanRankCorrelationSignificance().

Obrigado pelo link, vou me lembrar dele.

 
fxsaber:

ZY Declaração incorreta

R^2 nada mais é do que a correlação entre um gráfico e seu modelo linear

//-- Encontre R^2 e seu sinal
   double r2 = MathPow(corr, 2.0);

Sim, de fato, um erro grosseiro de redação. Estou surpreso por ter escrito tal coisa. Vou corrigi-lo.

Quando você olha para todos os outros códigos MQL, não entende por que eles são fornecidos, pois são completamente ilegíveis sem o conhecimento do CStrategy

O CStrategy é necessário apenas para coletar os requisitos. O código principal, como foi corretamente observado, é o cálculo real do R2.

O código para calcular o "patrimônio", adequado para R^2. Ele está escrito no estilo MT4, não é difícil traduzi-lo para o MT5....

Vamos estudá-lo.

 
Maxim Dmitrievsky:

Concordo com isso, todo o resto terá de ser retirado das classes para ser adicionado ao seu sistema... seria melhor ter tudo em f-iases separadas ou em um includnik separado.

Pegue seu cálculo de patrimônio líquido (ou o código apresentado por fxsaber) como uma matriz dupla e insira-o na função de cálculo R^2. Você não precisa extrair nada, não precisa usar classes e CStrategy.

 
fxsaber:

Não entendo como o R^2 pode assumir valores negativos, conforme mostrado no segundo gráfico? Também há dúvidas com relação ao primeiro gráfico. Se a regressão linear for construída corretamente, parece que o LR de Pearson não deveria ser negativo. Mas no gráfico ele não é. Onde estou errando?

Entendi. Não estou errado em lugar nenhum, apenas os gráficos têm R^2 e LR personalizados - a multiplicação por -1 do valor real ocorre se o último elemento da série numérica for menor que o primeiro. Seria bom escrever sobre isso antes dos gráficos.

Ele está oculto no artigo:

Nosso script calcula tanto a correlação LR quanto o R^2. Veremos a diferença entre eles um pouco mais adiante. Há uma pequena adição ao script. Multiplicaremos o coeficiente de correlação resultante pelo sinal final do gráfico sintético. Se o resultado for menor que zero, a correlação será negativa; se for maior, será positiva. Isso é feito para separar rápida e facilmente os resultados negativos dos positivos sem precisar recorrer a outras estatísticas. É assim que a Correlação LR funciona no MetaTrader 5, e o R^2 será construído de acordo com o mesmo princípio.

Talvez eu devesse ter escrito sobre isso em outro lugar e várias vezes.