preview
Прогнозирование валютных курсов с использованием классических методов машинного обучения: Логит и Пробит модели

Прогнозирование валютных курсов с использованием классических методов машинного обучения: Логит и Пробит модели

MetaTrader 5Эксперты | 4 октября 2024, 10:10
142 9
Evgeniy Chernish
Evgeniy Chernish

Введение

Перед исследователями финансовых рынков всегда будет стоять непростая задача выбора математической модели для прогнозирования будущего поведения торговых инструментов. На сегодняшний день разработано огромное количество таких моделей. Поэтому возникает вопрос как не утонуть во всем этом многообразии методов и подходов, с чего начать, на каких моделях лучше всего сконцентрироваться, особенно если вы только приступаете к прогнозированию с помощью моделей машинного обучения ? Если задачу прогноза попытаться свести к простому ответу на вопрос — «будет ли цена закрытия завтра выше чем цена закрытия сегодня ?», тогда логичным выбором будут бинарные модели классификации. Одними из самых простых и широко применяемых являются логит и пробит регрессия. Эти модели относятся к самой распространенной форме машинного обучения — так называемое обучение с учителем.

Задача обучения с учителем в свою очередь состоит в том, что бы научить нашу модель отображать множество входов {x} (предикторов или признаков) во множество выходов {y} (целей или меток). В данной работе предсказывать мы будем всего два рыночных состояния — рост или падение цены валютной пары. Следовательно, будем иметь всего два класса меток y ∊ {1,0}. В качестве предикторов выступят ценовые паттерны, а именно стандартизированные приращения цен с определенным лагом. Эти данные сформируют наше обучающее множество {x, y}, на котором мы будем оценивать параметры наших моделей. Прогнозная модель основанная на обученных классификаторах реализована в виде эксперта LogitExpert.


Бинарная Логит и Пробит регрессия

Очень кратко пройдемся по теоретической части. Стоит сказать, что простейшей моделью двоичного выбора является линейная вероятностная модель в которой вероятность успешного события P(yn=1|xn) является линейной функцией объясняющих переменных:

P(yn=1|xn) = w0*1 + w1x1 + w2x2 + … + wkxk

К сожалению, такая модель имеет очень серьезный недостаток — предсказанная величина может быть больше единицы или меньше нуля, а это в свою очередь не позволяет интерпретировать прогнозное значение как вероятность. Поэтому для решения данной проблемы было предложено использовать известные функции распределения вероятностей в которые бы подставлялись значения линейной функции.

Пробит-модель основана на стандартном нормальном законе распределения N(0,1):

P(yn=1|xn) = F(xnw)=μn

  • n – нижний индекс обозначающий номер наблюдения (примера),

  • yn - метка класса,

  • F( ) - функция распределения нормального закона (функция активации),

  • xn – вектор признаков,

  • w – вектор параметров модели,

  • xnw –логит или предактивация (представляет собой скалярное произведение вектора признаков и вектора параметров)

xnw = w0*1 + w1x1 + w2x2 + … + wkxk

В свою очередь логит-модель основывается на логистическом законе распределения вероятностей:

                                                            P(yn=1|xn) = L(xnw) = exp(xnw)/(1 + exp(xnw)) = μn

Функции распределения логистического и нормального распределения достаточно близки, а на интервале [-1,2;1,2] они практически одинаковы. Поэтому логит и пробит модели часто дают похожий результат, если только вероятность не близка к нулю или единице. Таким образом данные модели при подстановке в них вектора признаков позволяют нам вычислить вероятности меток классов, а следовательно и вероятность будущего направления движения цены.


Подготовка данных

Перед тем как оценить параметры модели, нам необходимо определиться с признаками, после чего стандартизировать их и представить в надлежащем виде для функции которая будет находить оптимальные параметры(в смысле минимизации функции потерь). За эту работу отвечает функция GetDataset:

  • InpCount_ - указываем количество примеров для обучения

  • lag_ - количество анализируемых признаков (приращения цены с лагом)

  • string X — валютная пара по которой вычисляются признаки

  • string y — валютная пара по которой вычисляются метки

  • int start — номер бара с которого начинаем брать примеры для обучения

В качестве иллюстративного примера признаками у нас послужат лаговые приращения цены валютной пары. Например, если установить аргумент функции lag_ = 4, тогда признаками будут x{return-4,return-3,return-2,return-1} и таких примеров для обучения у нас будет ровно (InpCount_- lag_) элементов.

//+------------------------------------------------------------------+
//|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;
  }

На выходе получаем матрицу признаков Input_X и вектор меток Target_y. После того как обучающее множество сформировано, переходим к оценке параметров.


Оценка параметров модели

Чаще всего оценки параметров находят с помощью метода максимального правдоподобия. В бинарном случае логит и пробит модели предполагают, что зависимая переменная имеет распределение Бернулли. А раз так, тогда логарифмическая функция правдоподобия будет равна:

LLF

  • yn – метка класса,

  • μn - вероятность предсказания класса, с помощью логит или пробит регрессии,

  • N – количество обучающих примеров

Для оценки параметров нам необходимо найти максимум данной функции, но так как в машинном обучении принято минимизировать функцию потерь, да и все оптимизаторы в основном настроены на минимизацию целевых функций то к функции правдоподобия просто добавляют знак минус. В итоге получают так называемое отрицательное логарифмическое правдоподобие (NLL). Минимизировать данную функцию потерь будем с помощью квази-ньютоновского метода оптимизации второго порядка L-BFGS реализованного в библиотеке Alglib. Именно этот численный метод, как правило, используется для нахождения параметров логит и пробит моделей. Другой популярный метод оптимизации — метод наименьших квадратов с итеративным пересчетом весов (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;
  }

Однако вычисления одних оценок параметров недостаточно, хотелось бы еще получить стандартные ошибки этих оценок, что бы понимать насколько значимы являются наши признаки.

Например, популярная библиотека машинного обучения scikit-learn почему-то данную информацию для логит-модели не рассчитывает. Я реализовал вычисление стандартных ошибок как для логит так и для пробит модели, так что теперь можно посмотреть оказывают ли какие-то конкретные признаки значимое со статистической точки зрение влияние на прогноз или нет. Это одна из причин почему я предпочитаю написать код логит модели самостоятельно в MQL, а не воспользоваться ONNX конвертацией готовых моделей из популярных пакетов машинного обучения. Другая причина, мне необходима динамическая модель, которая бы могла реоптимизировать параметры классификатора на каждом баре или с желаемой заданной частотой.

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

NLL_L2

  • λ = 1/С , С = (0,1]

Здесь мы просто прибавляем к нашей существующей функции потерь квадрат нормы вектора параметров умноженный на гиперпараметр λ лямбда. Чем больше лямбда тем больше штрафуются параметры за большие величины и тем сильнее регуляризация.

За оценку параметров классификатора отвечает функция FitLogitRegression:

  • bool L2 = false — по умолчанию L2 регуляризация отключена,
  • double C=1.0 — гиперпараметр силы регуляризации, чем он меньше тем сильнее ограничиваются значения оптимизируемых параметров,
  • bool probit = false — по умолчанию включена логит модель,
  • double alpha — уровень значимости alpha Хи-квадрат распределения статистики LR

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

//+------------------------------------------------------------------+
//| 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;
  }

После того как параметры найдены и вычислены их ковариационные матрицы можем переходить к прогнозированию.


Прогнозирование

За прогнозирование меток классов и, следовательно, сигналов на покупку или продажу отвечает функция Trade_PredictedTarget. Она получает на вход оптимизируемые параметры и выдает спрогнозированную метку класса. После этого экспертом LogitExpert формируются правила на открытие позиций. Они довольно простые. Если мы получили сигнал на покупку(signal = 1) — открываем длинную позицию. Если длинная позиция уже существует продолжаем ее удерживать. При поступлении сигнала на продажу, длинная позиция закрывается и сразу  открывается короткая позиция.

Собственно сам код эксперта 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());
  }

Что отличает этот эксперт от ряда других подходов ? Во-первых, он позволяет проводить переоптимизацию параметров классификатора каждые (reoptimize_step) баров. Во-вторых, он не просто оценивает параметры модели, он уделяет внимание стандартным ошибкам этих оценок, на что очень часто многие не обращают внимания. Мало найти «оптимальные» параметры по выборке, необходимо еще проверить насколько значимы эти параметры или модель в целом. Ведь если параметры не значимы, тогда такой торговый сигнал логичнее было бы проигнорировать не так ли?

Поэтому, в данном эксперте заложена также процедура проверки гипотезы модели на значимость. При этом нулевая гипотеза будет утверждать, что все параметры модели равны нулю (H0:w1=0,w2=0,w3=0,..., wk=0), а альтернативная гипотеза H1 - что некоторые параметры не равны нулю и, следовательно, модель полезна в предсказании. Для проверки такой гипотезы используется критерий отношения правдоподобия(LR), который оценивает разность между предполагаемой и тривиальной моделью:


LR = 2(LLF – LLF0)

  • LLF – найденное значение логарифма функции правдоподобия,

  • LLF0 - логарифм правдоподобия при нулевой гипотезе, то есть для тривиальной модели

p0 = ∑(yn =1)/N  – выборочная частота успеха,

LLF0 = N(p0*Ln(p0) + (1- p0)*Ln(1 – p0))

Чем больше разность тем лучше полная модель перед тривиальной. При выполнении нулевой гипотезы статистика LR имеет Хи-квадрат распределение с v-степенями свободы(v равно количеству признаков). Если вычисленное значение статистики LR попадает в критическую область, то есть LR > X2крит (alpha; v=lag_), то гипотеза H0 отвергается, а следовательно торговый сигнал не игнорируется и открывается торговая позиция.

Один из возможных вариантов развития событий. GBPUSD, Daily

Backtest GBPUSD Daily

Гиперпараметры

hyperparameters

Стоит сказать, что помимо оценки параметров непосредственно самих моделей классификаторов у нас еще присутствует большой багаж гиперпараметров:

  • глубина истории
  • количество признаков
  • уровень значимости alpha
  • шаг реоптимизации

Подбор этих гиперпараметров осуществляется в тестере стратегий MetaTrader 5. Одна из задач, которая может улучшить производительность эксперта — это построить функцию зависимости параметра глубины истории от текущего состояния рынка, то есть сделать его динамическим так же, как мы это сделали с параметрами логит и пробит модели. Но это уже другая история. В качестве подсказки смотрите мою статью Критерий однородности Смирнова как индикатор нестационарности временного ряда, где рассматривается вопрос построения индикатора разладки.


Заключение

В данной статье мы рассмотрели регрессионные модели с бинарными результативными показателями, научились оценивать параметры этих моделей, а также реализовали торговый эксперт LogitExpert для тестирования и настройки этих моделей. Его особенность заключается в том, что он позволяет на лету переобучать параметры классификатора, основываясь на самых последних и актуальных данных.

Особое внимание было уделено оценке стандартных ошибок параметров, для чего потребовалось оценить ковариационные матрицы для логит и пробит моделей.

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

Прикрепленные файлы |
LogitReg.mqh (22.43 KB)
LogitExpert.mq5 (10.94 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (9)
Stanislav Korotky
Stanislav Korotky | 4 окт. 2024 в 16:41
Evgeniy Chernish #:
Все вопросы к его величеству рынку форекс и гипотезе эффективного рынка. 

Название тогда вводит в заблуждение.

Evgeniy Chernish
Evgeniy Chernish | 4 окт. 2024 в 17:12
Aleksey Nikolayev #:

Спасибо, хорошая интересная статья.

Имхо, на днёвках можно уже пытаться использовать фундаментальные данные. Это не в смысле критики статьи, а в порядке размышлений. Интересно, как вообще можно к ценовым данным адекватно "подмешивать" макроэкономические данные. Проблема в их редкой смене, например. Наверно, можно ещё макроэкономику как-то использовать при предобработке цен - переход от номинальных курсов к реальным, например.

Спасибо Алексей ! Честно говоря никогда не интересовался фундаментом и не потому что он не может дать дополнительную информацию , а просто потому что невозможно объять необъятное. Поэтому я даже не смотрю пока в эту сторону.
Evgeniy Chernish
Evgeniy Chernish | 4 окт. 2024 в 17:17
Stanislav Korotky #:

Название тогда вводит в заблуждение.

Почему ? Используется классификационная предсказывающая модель, которая даёт прогнозы. Считает корректно то что в эту модель засыпали. Что не так тогда ? Что модель не может побить наивный прогноз ? Я этого не обещал )
Stanislav Korotky
Stanislav Korotky | 4 окт. 2024 в 17:48
Evgeniy Chernish #:
Почему ? Используется классификационная предсказывающая модель, которая даёт прогнозы. Считает корректно то что в эту модель засыпали. Что не так тогда ? Что модель не может побить наивный прогноз ? Я этого не обещал )

"Невозможность прогнозирования валютных курсов с использованием классических методов..."

Evgeniy Chernish
Evgeniy Chernish | 4 окт. 2024 в 18:20
Stanislav Korotky #:

"Невозможность прогнозирования валютных курсов с использованием классических методов..."

Мне даже в голову не пришло, что это невозможно. Я просто взял и спрогнозировал, сверился с python библиотекой, что бы ошибки проверить. Ну вот такая эффективность получилась, что уж тут сделаешь.Возможно кто-то добавит какие-то фильтры, свои признаки, может у кого-то лучше получится. А вы сразу невозможность.
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Нейросети в трейдинге: Управляемая сегментация Нейросети в трейдинге: Управляемая сегментация
Предлагаем познакомиться с методом комплексного мультимодального анализа взаимодействия и понимания признаков.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Введение в MQL5 (Часть 7): Руководство для начинающих по созданию советников и использованию кода от ИИ в MQL5 Введение в MQL5 (Часть 7): Руководство для начинающих по созданию советников и использованию кода от ИИ в MQL5
В этой статье мы представим полное руководство для начинающих по созданию советников (EA) на MQL5. Вы найдете пошаговые инструкции по созданию экспертов с использованием псевдокода и возможностей кода, сгенерированного ИИ. Эта статья предназначена для тех, кто только начинает свой пусть в алготрейдинге, а также для всех, кто хочет улучшить навыки разработки эффективных советников.