Обсуждение статьи "R-квадрат как оценка качества кривой баланса стратегии"

 

Опубликована статья R-квадрат как оценка качества кривой баланса стратегии:

Статья описывает построение пользовательского критерия оптимизации R-квадрат. По этому критерию можно оценить качество кривой баланса стратегии и выбрать наиболее равномерно растущие и стабильные стратегии. Материал описывает принципы его построения и статистические методы, используемые для оценки свойств и качества этой метрики.

Линейной регрессией называется линейная зависимость одной переменной y от другой независимой переменной x, выраженная формулой y = ax+b. В этой формуле а — множитель, b — коэффициент смещения. В действительности независимых переменных может быть несколько, и такую регрессионную модель называют мультирегрессионной. Однако мы рассмотрим только самый простой случай. 

Линейную зависимость можно наглядно представить в виде простого графика. Возьмем дневной график EURUSD с 2017.06.21 по 2017.09.21. Этот участок выбран не случайно: именно в этот период времени на этой валютной паре наблюдался умеренный восходящий тренд. В MetaTrader это выглядит так:

Рис. 11. Ценовая динамика EURUSD с 21.06.2017 по 21.08.2017, дневной таймфрейм

Автор: Vasiliy Sokolov

 

Именно поэтому количество сделок должно быть достаточно большим. Но что подразумевать под достаточностью? Принято считать, что любая выборка должна содержать как минимум 37 измерений. Это магическое число в статистике, именно оно является нижней границей репрезентативности параметра. Конечно, для оценки торговой системы этого количества сделок недостаточно. Для надежного результата желательно совершить не менее 100 — 150 сделок. Более того, для многих профессиональных трейдеров и этого недостаточно. Они проектируют системы, совершающие не менее 500-1000 сделок, и уже потом, на основании этих результатов, рассматривают возможность запуска системы на реальных торгах...

О как! Всегда думал, что 100. Спасибо, интересная статья.

Таким образом, можно с уверенностью сказать, что коэффициент детерминации R-квадрат — важное дополнение к уже существующему набору метрик тестирования MetaTrader 5. Он позволяет оценить гладкость кривой линии баланса стратегии, что само по себе является нетривиальным показателем. R-квадрат прост в использовании: его диапазон значений фиксирован и находится в пределах от -1.0 до +1.0, сигнализируя об отрицательной тенденции баланса стратегии (значения близкие к -1.0), отсутствию тенденции (значения близкие к 0.0) и положительном тренде (значения стремящиеся к +1.0). Благодаря всем этим свойствам, надежности и простоте, R-квадрат может быть рекомендован к использованию при построении прибыльной торговой системы.

О как! Всегда думал, что Ryx Коэффициент детерминации - служит для оценки качества линейной регрессииКоэффициент детерминации для модели с константой принимает значения от 0 до 1

Ещё принято проводить тесты на значимость коэффициента регрессии. Даже в Алглиб они есть :-)

PearsonCorrelationSignificance(), SpearmanRankCorrelationSignificance().

 
 воспользуемся уже готовым экспертом CImpulse 2.0, работа которого описана в статье "Универсальный торговый эксперт: работа с отложенными ордерами". Он был выбран за простоту и за то, что, в отличие от экспертов из стандартной поставки MetaTrader 5, поддается оптимизации, что для целей нашей статьи крайне важно

Что имелось в виду?

В отчете терминала MetaTrader есть специальная статистическая метрика. Она называется LR Correlation и показывает корреляцию между линией баланса и линейной регрессией, найденной для этой линии.

Способ расчета коэффициента детерминации R^2 аналогичен способу расчета LR Correlation. Но итоговое число дополнительно возводится в квадрат.

Здесь нас больше всего интересует одно число — R-squared или R-квадрат. Эта метрика показывает значение 0.5903. Следовательно, линейная регрессия объясняет 59,03% всех значений, а остальные 41% остаются необъясненными. 

Из приведенных цитат следует, что R^2 = LR^2. При этом критерий поиска линейной функции под названием "Линейная регрессия" является МНК отклонений или что одно и то же - максимизация абсолютного значения КК Пирсона, что есть MathAbs(LR). А максимизация MathAbs(LR) - то же самое, что максимизация R^2, т.к. MathAbs(LR) = MathSqrt(R^2).


Итого имеем, что найденная линейная регрессия находится по критерию максимизации MathAbs(R)^n, где n - любое положительное число.

Ну тогда какой смысл говорить о 59.03% всех значений, объясняемых ЛР, когда можно, например, при n = 1 получить 76.8%, а при n = 4 - 34.8%?


ЗЫ Неверное утверждение

R^2 — не что иное, как корреляция между графиком и его линейной моделью

   //-- Находим R^2 и его знак
   double r2 = MathPow(corr, 2.0);
 
Способ расчета коэффициента детерминации R^2 аналогичен способу расчета LR Correlation. Но итоговое число дополнительно возводится в квадрат.

Графики распределений LR Correlation и R^2 для 10 000 независимых примеров, что представлены в статьей, показывают, что R^2 != LR^2.

Удивительно то, что простым математическим действием (возведением во вторую степень) мы полностью убрали нежелательные краевые эффекты распределения.

Не понимаю, почему вторая степень исходного "вогнутого" распределения делает его "ровным"?
 
Теперь найдем лучший прогон по параметру R-квадрат. Для этого сохраним прогоны оптимизации в XML-файл. Если на компьютере установлен Microsoft Excel, то файл откроется в нем автоматически. Мы будем работать с сортировкой и фильтрами, поэтому выделим заголовок таблицы и нажмем одноименную кнопку (Главная -> Сортировка и фильтр -> Фильтр), после чего появится возможность гибкого отображения столбцов. Отсортируем прогоны по пользовательскому критерию оптимизации

Зачем для этого Excel, когда все сортируется в самом Тестере?

 
НедостаткиРешение
Применим исключительно для оценки линейных процессов, или систем, торгующих фиксированным лотом.Не применять для торговых систем, использующих систему капитализации (мани-менеджемент).

Надо эквити для вычисления R^2 считать ни как AccountEquity ( == AccountBalance + Sum(Profit[i])),  а как Sum(Profit[i] / Lots[i]) (для односимвольных ТС).

 
Из всех MQL-исходников в статье полезен только один
//+------------------------------------------------------------------+
//| Возвращает оценку R^2, рассчитанную на основе equity стратегии   |
//| Значения equity передается в качестве массива equity             |
//+------------------------------------------------------------------+
double CustomR2Equity(double& equity[], ENUM_CORR_TYPE corr_type = CORR_PEARSON)
{
   int total = ArraySize(equity);
   if(total == 0)
      return 0.0;
   //-- Заполняем матрицу Y - значение equity, X - порядковый номер значения
   CMatrixDouble xy(total, 2);
   for(int i = 0; i < total; i++)
   {
      xy[i].Set(0, i);
      xy[i].Set(1, equity[i]);
   }
   //-- Находим коэффициенты a и b линейной модели y = a*x + b;
   int retcode = 0;
   double a, b;
   CLinReg::LRLine(xy, total, retcode, a, b);
   //-- Генерируем значения линейной регрессии для каждого X;
   double estimate[];
   ArrayResize(estimate, total);
   for(int x = 0; x < total; x++)
      estimate[x] = x*a+b;
   //-- Находим коэффициент корреляции значений с их же линейной регрессией
   double corr = 0.0;
   if(corr_type == CORR_PEARSON)
      corr = CAlglib::PearsonCorr2(equity, estimate);
   else
      corr = CAlglib::SpearmanCorr2(equity, estimate);
   //-- Находим R^2 и его знак
   double r2 = MathPow(corr, 2.0);
   int sign = 1;
   if(equity[0] > equity[total-1])
      sign = -1;
   r2 *= sign;
   //-- Возвращаем нормализованную оценку R^2, с точностью до сотых
   return NormalizeDouble(r2,2);
}

Он универсальный - подходит для любых double-массивов (не только Эквити).

Когда смотришь все остальные MQL-коды, то не понимаешь, зачем они приведены, т.к. они совершенно не читабельны без владения CStrategy.


В целом Спасибо автору за статью, заставила задуматься.


ЗЫ Выделенные желтым строки в исходнике спорны.

 

Очень интересно, спасибо. Раньше не задумывался о том что метрики тестера искажаются при каптитализации, в связи с этим оптимизация будет менее эффективной нежели при фикс. объеме. Ну и R^2 конечно очень полезна, интересно, ускорит ли это сам процесс оптимизации по сравнению с profit factor+ max balance, например.

 
fxsaber:
Из всех MQL-исходников в статье полезен только один

С этим согласен, все остальное придется выдирать из классов, для добавления в свою систему.. лучше бы все в отдельных ф-ях было или отдельный инклудник.

 
fxsaber:

Надо эквити для вычисления R^2 считать ни как AccountEquity ( == AccountBalance + Sum(Profit[i])),  а как Sum(Profit[i] / Lots[i]) (для односимвольных ТС).

Код для расчета "эквити", годного для R^2. Написан в MT4-стиле, в MT5 перевести несложно...

// Расчет эквити без ММ (вариант для односимвольных ТС)
class EQUITY
{
protected:
  int PrevHistoryTotal;
  double Balance;
  double PrevEquity;
  
  // Добавление элемента в конец произвольного массива
  template <typename T>
  static void AddArrayElement( T &Array[], const T Value, const int Reserve = 0 )
  {
    const int Size = ::ArraySize(Array);
  
    ::ArrayResize(Array, Size + 1, Reserve);
  
    Array[Size] = Value;
  }

  static double GetOrderProfit( void )
  {
    return((OrderProfit()/* + OrderCommission() + OrderSwap()*/) / OrderLots()); // комиссия и своп иногда полезно игнорировать
  }
  
  static double GetProfit( void )
  {
    double Res = 0;
    
    for (int i = OrdersTotal() - 1; i >= 0; i--)
      if (OrderSelect(i, SELECT_BY_POS) && (OrderType() <= OP_SELL))
        Res += EQUITY::GetOrderProfit();
    
    return(Res);
  }
  
  double GetBalance( void )
  {
    const int HistoryTotal = OrdersHistoryTotal();
    
    if (HistoryTotal != this.PrevHistoryTotal)
    {
      for (int i = HistoryTotal - 1; i >= PrevHistoryTotal; i--)
        if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) && (OrderType() <= OP_SELL) && OrderLots()) // OrderLots - CloseBy
          this.Balance += EQUITY::GetOrderProfit();
      
      this.PrevHistoryTotal = HistoryTotal;
    }
    
    return(this.Balance);
  }
  
public:
  double Data[];

  EQUITY( void ) : PrevHistoryTotal(0), Balance(0), PrevEquity(0)
  {
  }
  
  virtual void OnTimer( void )
  {
    const double NewEquity = this.GetBalance() + EQUITY::GetProfit();
    
    if (NewEquity != this.PrevEquity)    
    {
      EQUITY::AddArrayElement(this.Data, NewEquity, 1e4);
      
      this.PrevEquity = NewEquity;
    }
  }
};


Использование

EQUITY Equity;

void OnTimer()
{
  Equity.OnTimer();
}

double OnTester()
{
  return(CustomR2Equity(Equity.Data));
}
 
fxsaber:

Код для расчета "эквити", годного для R^2. Написан в MT4-стиле, в MT5 перевести несложно...


Использование


Классно, можно же просто на каждом новом баре вызывать, что бы систему таймером не грузить. Для систем с контролем нового бара.