Возможности Мастера MQL5, которые вам нужно знать (Часть 1): Регрессионный анализ
1. Введение
Мастер MQL5 позволяет быстро создавать и запускать советников, так как большинство второстепенных аспектов торговли предварительно закодированы в библиотеке MQL5. Это позволяет трейдерам сосредоточиться на специальных аспектах своей торговли, таких как условия входа и выхода. В библиотеку включены некоторые классы сигналов входа и выхода, такие как сигналы индикаторов Accelerator Oscillator, Adaptive Moving Average и многих других. Однако они основаны на запаздывающих индикаторах. К тому же, их довольно сложно преобразовать в успешные стратегии. Поэтому особенно важна возможность создавать собственные сигналы. В этой статье мы рассмотрим, как это можно сделать с помощью регрессионного анализа.
2. Создание класса
2.1 Согласно Википедии, регрессионный анализ – это набор статистических методов исследования влияния одной или нескольких независимых переменных на зависимую переменную. Он может быть полезен для пользовательских советников, поскольку ценовые данные представляют собой временной ряд. Анализ позволяет проверить способность предыдущих цен и изменений цен влиять на будущие цены и изменения цен. Регрессионный анализ может быть представлен следующим уравнением где y – зависимая и, следовательно, прогнозируемая переменная зависит от предшествующих значений x, каждый со своим коэффициентом β и ошибкой ε. Значения x и y можно рассматривать как предыдущий и прогнозируемый ценовой уровень соответственно. Помимо работы с уровнями цен, аналогичным образом можно исследовать и изменения цен. Неизвестное y зависит от xs, βs и ε. Из них, правда, известны только xs и β0 (y-пересечение). y-пересечение известно, потому что это цена непосредственно перед x i 1. Поэтому нам нужно найти соответствующие βs для каждого x, а затем для ε. Так как каждый x i 1 был y I в предшествующий момент времени временного ряда, мы можем найти решение для значений β, используя одновременные уравнения. Например, если следующее изменение цены зависит только от двух предыдущих изменений, наше текущее уравнение может быть таким:
А предыдущие уравнения будут такими:
Так как мы оцениваем ошибку ε отдельно, мы можем решить два одновременных уравнения для значений β. Нумерация значений x в формуле Википедии не соответствует формату "ряда" MQL5, то есть x с наибольшим номером является самым последним. Таким образом, я перенумеровал значения x в приведенных выше двух уравнениях, чтобы показать, как переделать их в одновременные. Мы снова начинаем с y-пересечений xi1 и xi0 для представления β0 в уравнении 1. Решение одновременных уравнений лучше обрабатывается с помощью матриц для большей эффективности. Необходимые инструменты есть в библиотеке MQL5.
2.2 Библиотека MQL5 имеет обширную коллекцию классов по статистике и общие алгоритмы, которые явно исключают необходимость их кодирования с нуля. Код библиотеки открыт, так что во всем можно убедиться самостоятельно. Для наших целей мы будем использовать функцию RMatrixSolve в классе CDenseSolver в файле solvers.mqh. В основе этой функции лежит использование разложения матрицы LU для быстрого и эффективного поиска решения для значений β. На эту тему в архиве MetaQuotes можно найти статьи. В Википедии также есть объяснение.
Прежде чем мы займемся поиском решений для значений β, было бы полезно посмотреть, как устроен класс CExpertSignal, поскольку он является основой для нашего класса. Почти во всех классах сигналов советников, которые можно собрать в Мастере, имеются функции LongCondition и ShortCondition. Они возвращают значение, которое устанавливает, следует ли нам открывать длинную или короткую позицию соответственно. Это значение должно быть целым числом в диапазоне от 0 до 100, чтобы соответствовать входным параметрам Мастера Signal_ThresholdOpen и Signal_ThresholdClose. Как правило, при торговле нам необходимо, чтобы наши условия закрытия позиции были менее консервативными, чем условия открытия. Это означает, что порог открытия будет выше порога закрытия. Таким образом, при разработке сигнала у нас будут входные параметры для вычисления порога закрытия и отдельные, но аналогичные входные параметры для порога открытия. Выбор входных данных при вычислении условия будет определяться тем, есть ли у нас открытые позиции. Если у нас есть открытые позиции, мы будем использовать параметры закрытия. Если позиций нет, будем использовать параметры открытия. Ниже представлен листинг интерфейса сигнального класса нашего советника, в котором имеются эти два набора параметров.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CSignalDUAL_RA : public CExpertSignal { protected: CiMA m_h_ma; // highs MA handle CiMA m_l_ma; // lows MA handle CiATR m_ATR; //--- adjusted parameters int m_size; double m_open_determination,m_close_determination; int m_open_collinearity,m_open_data,m_open_error; int m_close_collinearity,m_close_data,m_close_error; public: CSignalDUAL_RA(); ~CSignalDUAL_RA(); //--- methods of setting adjustable parameters //--- PARAMETER FOR SETTING THE NUMBER OF INDEPENDENT VARIABLES void Size(int value) { m_size=value; } //--- PARAMETERS FOR SETTING THE OPEN 'THRESHOLD' FOR THE EXPERTSIGNAL CLASS void OpenCollinearity(int value) { m_open_collinearity=value; } void OpenDetermination(double value) { m_open_determination=value; } void OpenError(int value) { m_open_error=value; } void OpenData(int value) { m_open_data=value; } //--- PARAMETERS FOR SETTING THE CLOSE 'THRESHOLD' FOR THE EXPERTSIGNAL CLASS void CloseCollinearity(int value) { m_close_collinearity=value; } void CloseDetermination(double value) { m_close_determination=value; } void CloseError(int value) { m_close_error=value; } void CloseData(int value) { m_close_data=value; } //--- method of verification of settings virtual bool ValidationSettings(void); //--- method of creating the indicator and timeseries virtual bool InitIndicators(CIndicators *indicators); //--- methods for detection of levels of entering the market virtual bool OpenLongParams(double &price,double &sl,double &tp,datetime &expiration); virtual bool OpenShortParams(double &price,double &sl,double &tp,datetime &expiration); //--- methods of checking if the market models are formed virtual int LongCondition(void); virtual int ShortCondition(void); protected: //--- method of initialization of the oscillator bool InitRA(CIndicators *indicators); //--- methods of getting data int CheckDetermination(int ind,bool close); double CheckCollinearity(int ind,bool close); // double GetY(int ind,bool close); double GetE(int ind,bool close); double Data(int ind,bool close); // };
Ниже представлен листинг функций LongCondition и ShortCondition.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CSignalDUAL_RA::LongCondition(void) { int _check=CheckDetermination(0,PositionSelect(m_symbol.Name())); if(_check>0){ return(_check); } return(0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CSignalDUAL_RA::ShortCondition(void) { int _check=CheckDetermination(0,PositionSelect(m_symbol.Name())); if(_check<0){ return((int)fabs(_check)); } return(0); }
Чтобы найти решение для значений β, используем функцию GetY. Листинг представлен ниже.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CSignalDUAL_RA::GetY(int ind,bool close) { double _y=0.0; CMatrixDouble _a;_a.Resize(m_size,m_size); double _b[];ArrayResize(_b,m_size);ArrayInitialize(_b,0.0); for(int r=0;r<m_size;r++) { _b[r]=Data(r,close); for(int c=0;c<m_size;c++) { _a[r].Set(c,Data(r+c+1, close)); } } int _info=0; CDenseSolver _S; CDenseSolverReport _r; double _x[];ArrayResize(_x,m_size);ArrayInitialize(_x,0.0); _S.RMatrixSolve(_a,m_size,_b,_info,_r,_x); for(int r=0;r<m_size;r++) { _y+=(Data(r,close)*_x[r]); } //--- return(_y); }
Функция Data будет переключаться между изменениями цены закрытия торгуемого символа или изменениями скользящей средней той же цены закрытия. Используемая опция будет определяться входным параметром m_open_data или m_close_data в зависимости от того, вычисляем ли мы порог открытия или порог закрытия. Листинг для выбора данных показан в перечислении ниже.
enum Edata { DATA_TREND=0, // changes in moving average close DATA_RANGE=1 // changes in close };
Ниже приведена функция Data, выбирающая данные.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CSignalDUAL_RA::Data(int ind,bool close) { if(!close) { if(Edata(m_open_data)==DATA_TREND) { m_h_ma.Refresh(-1); return((m_l_ma.Main(StartIndex()+ind)-m_l_ma.Main(StartIndex()+ind+1))-(m_h_ma.Main(StartIndex()+ind)-m_h_ma.Main(StartIndex()+ind+1))); } else if(Edata(m_open_data)==DATA_RANGE) { return((Low(StartIndex()+ind)-Low(StartIndex()+ind+1))-(High(StartIndex()+ind)-High(StartIndex()+ind+1))); } } else if(close) { if(Edata(m_close_data)==DATA_TREND) { m_h_ma.Refresh(-1); return((m_l_ma.Main(StartIndex()+ind)-m_l_ma.Main(StartIndex()+ind+1))-(m_h_ma.Main(StartIndex()+ind)-m_h_ma.Main(StartIndex()+ind+1))); } else if(Edata(m_close_data)==DATA_RANGE) { return((Low(StartIndex()+ind)-Low(StartIndex()+ind+1))-(High(StartIndex()+ind)-High(StartIndex()+ind+1))); } } return(0.0); }
Как только мы получим значения β, мы можем перейти к расчету ошибки.
2.3 Стандартная ошибка, согласно Википедии, рассчитывается по формуле, приведенной ниже.
где s – стандартное отклонение, а n – размер выборки. Ошибка служит напоминанием о том, что не все прогнозы, какими бы тщательными они ни были, всегда будут абсолютно точными. Мы всегда должны ожидать и учитывать ошибки с нашей стороны. Стандартное отклонение, показанное в формуле, будет измеряться между нашими прогнозируемыми и фактическими значениями. Для сравнения мы также можем посмотреть на последнюю разницу между нашим прогнозным и фактическим значениями. Эти два параметра можно выбрать из перечисления ниже.
enum Eerror { ERROR_LAST=0, // use the last error ERROR_STANDARD=1 // use standard error }
Затем функция GetE возвращает нашу оценку ошибки в зависимости от входного параметра m_open_error или m_close_error при использовании приведенной выше формулы. Листинг представлен ниже.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CSignalDUAL_RA::GetE(int ind,bool close) { if(!close) { if(Eerror(m_open_error)==ERROR_STANDARD) { double _se=0.0; for(int r=0;r<m_size;r++) { _se+=pow(Data(r,close)-GetY(r+1,close),2.0); } _se=sqrt(_se/(m_size-1)); _se=_se/sqrt(m_size); return(_se); } else if(Eerror(m_open_error)==ERROR_LAST){ return(Data(ind,close)-GetY(ind+1,close)); } } else if(close) { if(Eerror(m_close_error)==ERROR_STANDARD) { double _se=0.0; for(int r=0;r<m_size;r++){ _se+=pow(Data(r,close)-GetY(r+1,close),2.0); } _se=sqrt(_se/(m_size-1)); _se=_se/sqrt(m_size); return(_se); } else if(Eerror(m_close_error)==ERROR_LAST){ return(Data(ind,close)-GetY(ind+1,close)); } } //--- return(Data(ind,close)-GetY(ind+1,close)); }
Как уже было сказано, использование m_open_error или m_close_error будет определяться тем, есть ли у нас открытые позиции. Получив оценку ошибки, мы сможем составить приблизительный прогноз для y. Однако регрессионный анализ имеет и свои подводные камни. Одним из них является способность независимых переменных быть слишком похожими и, следовательно, чрезмерно завышать прогнозируемое значение. Это явление называется коллинеарностью, и его стоит рассмотреть подробнее.
2.4 Коллинеарность (определение Википедии) можно охарактеризовать как наличие сильной взаимосвязи между двумя или более независимыми переменными в модели множественной регрессии (Investopedia). У коллинеарности нет формулы как таковой, и она обнаруживается по фактору инфляции дисперсии VIF (variance inflation factor). Этот фактор измеряется по всем независимым переменным (x), чтобы помочь понять, насколько каждая из этих переменных уникальна в прогнозировании y. Необходимая формула представлена ниже. В ней R - регрессия каждой независимой переменной по отношению к другим.
Принимая во внимание коллинеарность, для наших целей мы возьмем обратную корреляцию Спирмена между двумя последними наборами данных независимых переменных и нормализуем ее. Длина наших наборов данных будет задана входным параметром m_size, минимальная длина которого равна 3. При нормализации мы просто вычтем его из двух и инвертируем результат. Этот нормализованный вес затем можно умножить либо на оценку ошибки, либо на прогнозируемое значение, либо на то и другое, либо не использовать. Эти параметры указаны в перечислении ниже.
enum Echeck { CHECK_Y=0, // check for y only CHECK_E=1, // check for the error only CHECK_ALL=2, // check for both the y and the error CHECK_NONE=-1 // do not use collinearity checks };
Выбор применяемого веса также задается входным параметром m_open_collinearity или m_close_collinearity. Опять же, в зависимости от того, открыты ли позиции. Ниже приведен листинг CheckCollinearity.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CSignalDUAL_RA::CheckCollinearity(int ind,bool close) { double _check=0.0; double _c=0.0,_array_1[],_array_2[],_r=0.0; ArrayResize(_array_1,m_size);ArrayResize(_array_2,m_size); ArrayInitialize(_array_1,0.0);ArrayInitialize(_array_2,0.0); for(int s=0; s<m_size; s++) { _array_1[s]=Data(ind+s,close); _array_2[s]=Data(m_size+ind+s,close); } _c=1.0/(2.0+fmin(-1.0,MathCorrelationSpearman(_array_1,_array_2,_r))); double _i=Data(m_size+ind,close), //y intercept _y=GetY(ind,close), //product sum of x and its B coefficients _e=GetE(ind,close); //error if(!close) { if(Echeck(m_open_collinearity)==CHECK_Y){ _check=_i+(_c*_y)+_e; } else if(Echeck(m_open_collinearity)==CHECK_E){ _check=_i+_y+(_c*_e); } else if(Echeck(m_open_collinearity)==CHECK_ALL){ _check=_i+(_c*(_y+_e)); } else if(Echeck(m_open_collinearity)==CHECK_NONE){ _check=_i+(_y+_e); } } else if(close) { if(Echeck(m_close_collinearity)==CHECK_Y){ _check=_i+(_c*_y)+_e; } else if(Echeck(m_close_collinearity)==CHECK_E){ _check=_i+_y+(_c*_e); } else if(Echeck(m_close_collinearity)==CHECK_ALL){ _check=_i+(_c*(_y+_e)); } else if(Echeck(m_close_collinearity)==CHECK_NONE){ _check=_i+(_y+_e); } } //--- return(_check); }
Помимо проверки на коллинеарность, бывают случаи, когда регрессионный анализ не так предсказуем, как мог бы быть из-за внешних изменений на рынке. Чтобы отслеживать эти моменты и измерять способность независимых переменных нашего сигнала влиять на нашу зависимую переменную, мы используем коэффициент детерминации.
2.5 Коэффициент детерминации – статистическое измерение, которое исследует, как различия в одной переменной могут быть объяснены различием во второй переменной при прогнозировании исхода конкретного события (Investopedia). Википедия дает более исчерпывающее определение, и наши формулы, показанные ниже, взяты оттуда.
Формула суммы квадратов (где y - фактическое значение, а f - прогнозируемое),
Формула суммы конечных значений (где y - фактическое значение, а ÿ - скользящее среднее этих значений),
Сам коэффициент представляет собой R в квадрате.
Коэффициент измеряет степень, в которой xs влияет на y. Это важно, потому что, как уже упоминалось, бывают периоды, когда регрессия идет на убыль, а это означает, что безопаснее держаться подальше от рынков. Отслеживая это через фильтр, мы с большей вероятностью будем торговать, когда система надежна. Как правило, нам необходимо, чтобы коэффициент был выше 0, при этом 1 - идеальное значение. Входным параметром, используемым при определении нашего порога, будет m_open_determination или m_close_determination, опять же в зависимости от количества открытых позиций. Если коэффициент детерминации, вычисленный приведенной ниже функцией CheckDetermination, меньше этого параметра, то условия на покупку или продажу вернут ноль.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CSignalDUAL_RA::CheckDetermination(int ind,bool close) { int _check=0; m_h_ma.Refresh(-1);m_l_ma.Refresh(-1); double _det=0.0,_ss_res=0.0,_ss_tot=0.0; for(int r=0;r<m_size;r++) { _ss_res+=pow(Data(r,close)-GetY(r+1,close),2.0); _ss_tot+=pow(Data(r,close)-((m_l_ma.Main(r)-m_l_ma.Main(r+1))-(m_h_ma.Main(r)-m_h_ma.Main(r+1))),2.0); } if(_ss_tot!=0.0) { _det=(1.0-(_ss_res/_ss_tot)); if(_det>=m_open_determination) { double _threshold=0.0; for(int r=0; r<m_size; r++){ _threshold=fmax(_threshold,fabs(Data(r,close))); } double _y=CheckCollinearity(ind,close); _check=int(round(100.0*_y/fmax(fabs(_y),fabs(_threshold)))); } } //--- return(_check); }
Как только мы сможем проверить коэффициент детерминации, у нас будет рабочий сигнал. Далее следует интегрировать этот сигнал в советник с помощью Мастера MQL5.
3. Сборка с помощью Мастера MQL5
3.1 При сборке советника список пользовательских вспомогательных кодов можно использовать вместе с кодом Мастера MQL5. Это совершенно необязательно и зависит от стиля работы трейдера. Для целей этой статьи мы рассмотрим открытие пользовательского отложенного ордера на основе преобладающего ATR символа, а также трейлинг открытых позиций, основанный на том же индикаторе. Уровни тейк-профита использовать не будем.
3.1.1 Отложенные ордера на основе ATR можно установить, перегрузив функции OpenLongParams и OpenShortParams и настроив их в нашем классе сигналов, как показано ниже.
//+------------------------------------------------------------------+ //| Detecting the levels for buying | //+------------------------------------------------------------------+ bool CSignalDUAL_RA::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration) { CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL; //--- if(general==NULL) { m_ATR.Refresh(-1); //--- if a base price is not specified explicitly, take the current market price double base_price=(m_base_price==0.0) ? m_symbol.Ask() : m_base_price; //--- price overload that sets entry price to be based on ATR price =m_symbol.NormalizePrice(base_price-(m_price_level*(m_ATR.Main(0)/m_symbol.Point()))*PriceLevelUnit()); sl =0.0; tp =0.0; expiration+=m_expiration*PeriodSeconds(m_period); return(true); } //--- return(general.OpenLongParams(price,sl,tp,expiration)); } //+------------------------------------------------------------------+ //| Detecting the levels for selling | //+------------------------------------------------------------------+ bool CSignalDUAL_RA::OpenShortParams(double &price,double &sl,double &tp,datetime &expiration) { CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL; //--- if(general==NULL) { m_ATR.Refresh(-1); //--- if a base price is not specified explicitly, take the current market price double base_price=(m_base_price==0.0) ? m_symbol.Bid() : m_base_price; //--- price overload that sets entry price to be based on ATR price =m_symbol.NormalizePrice(base_price+(m_price_level*(m_ATR.Main(0)/m_symbol.Point()))*PriceLevelUnit()); sl =0.0; tp =0.0; expiration+=m_expiration*PeriodSeconds(m_period); return(true); } //--- return(general.OpenShortParams(price,sl,tp,expiration)); }
Советник, сгенерированный Мастером MQL5, имеет входной параметр Signal_PriceLevel. По умолчанию он равен нулю, но если ему присвоено значение, он регулирует расстояние в пунктах цены торгуемого символа от текущей цены, по которой будет размещен рыночный ордер. Когда этот параметр отрицательный, размещаются стоп-ордера. При размещении положительных лимитных ордеров параметр имеет тип данных double. Для наших целей этот параметр будет дробным или кратным текущим ценовым пунктам в ATR.
3.1.2 Класс трейлинга ATR также является настраиваемым классом CExpertTrailing, который использует ATR для установки и перемещения стоп-лосса. Реализация его ключевых функций представлена в листинге ниже.
//+------------------------------------------------------------------+ //| Checking trailing stop and/or profit for long position. | //+------------------------------------------------------------------+ bool CTrailingATR::CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp) { //--- check if(position==NULL) return(false); //--- m_ATR.Refresh(-1); double level =NormalizeDouble(m_symbol.Bid()-m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits()); //--- sl adjustment to be based on ATR double new_sl=NormalizeDouble(level-(m_atr_weight*(m_ATR.Main(0)/m_symbol.Point())),m_symbol.Digits()); double pos_sl=position.StopLoss(); double base =(pos_sl==0.0) ? position.PriceOpen() : pos_sl; //--- sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(new_sl>base && new_sl<level) sl=new_sl; //--- return(sl!=EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Checking trailing stop and/or profit for short position. | //+------------------------------------------------------------------+ bool CTrailingATR::CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp) { //--- check if(position==NULL) return(false); //--- m_ATR.Refresh(-1); double level =NormalizeDouble(m_symbol.Ask()+m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits()); //--- sl adjustment to be based on ATR double new_sl=NormalizeDouble(level+(m_atr_weight*(m_ATR.Main(0)/m_symbol.Point())),m_symbol.Digits()); double pos_sl=position.StopLoss(); double base =(pos_sl==0.0) ? position.PriceOpen() : pos_sl; //--- sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(new_sl<base && new_sl>level) sl=new_sl; //--- return(sl!=EMPTY_VALUE); }
m_atr_weight будет оптимизируемым параметром, как и m_price_level, устанавливая, насколько близко мы можем отслеживать открытые позиции.
3.2 Дальнейшая сборка в Мастере достаточно проста. Единственный заметный этап – выбор нашего сигнала, как показано ниже.
А также добавление нашего пользовательского метода трейлинга.
4. Тестирование в тестере стратегий
4.1 Вслед за сборкой советника в Мастере MQL5 следует компиляция для создания файла советника. Также это позволит убедиться, что в нашем коде нет ошибок.
4.2 Входные параметры советника по умолчанию также необходимо установить на вкладке входных параметров тестера стратегий. Необходимо убедиться, что Signal_TakeLevel и Signal_StopLevel равны нулю. Это связано с тем, что для целей этой статьи выход определяется только трейлинг-стопом или входным параметром Signal_ThresholdClose.
4.3 В идеале оптимизация должна выполняться на реальных тиках брокера, с которым вы собираетесь торговать. В этой статье мы оптимизируем EURUSD на 4-часовом таймфрейме в V-образном периоде с 2018.01.01 по 2021.01.01. Для сравнения мы запустим две оптимизации: первая будет использовать только рыночные ордера, а во второй будут разрешены отложенные ордера. Я не зря написал "будут разрешены". Мы по-прежнему будем рассматривать только использование рыночных ордеров, так как Signal_PriceLevel может быть равен нулю, поскольку оптимизация идет от отрицательного значения к положительному. Оптимизацию можно настроить заранее на случай использования отложенных ордеров. Единственное различие от варианта, не использующего отложенные ордера, заключается в том, что в последнем случае входной параметр Signal_PriceLevel останется равным 0 и не будет являться частью оптимизированных входных данных.
4.4 Ниже представлены результаты нашей оптимизации. Во-первых, это отчет и кривая эквити лучших результатов торговли только с рыночными ордерами.
Часть отчета 1
И аналогичный отчет и кривая эквити при использовании отложенных ордеров.
Часть отчета 2
Похоже, что наш сигнал регрессионного анализа выигрывает от использования отложенных ордеров, поскольку у него меньше просадок при немного меньшей прибыли. Систему можно попытаться улучшить, например путем изменения класса трейлинг-стопа или типа управления капиталом. Для наших целей тестирования мы использовали фиксированный процент маржи и оптимизировали наши критерии, установленные на "complex criterion". Я рекомендую как можно тщательнее проводить тестирование на исторических тиковых данных и выполнять достаточное количество форвард-тестов, прежде чем применять рекомендации, которые выходят за рамки этой статьи.
5. Заключение
5.1 Мастер MQL5 – мощный инструмент, который должен быть в арсенале каждого трейдера. В статье мы рассмотрели, как использовать некоторые статистические концепции регрессионного анализа, такие как коллинеарность и коэффициент детерминации, в качестве основы для надежной торговой системы. Следующими шагами будут обширное тестирование на исторических тиковых данных и изучение того, можно ли сочетать этот сигнал с другими уникальными сигналами, основанными на опыте трейдера или на встроенных сигналах библиотеки MQL5, при создании более полной торговой системы. Как и остальные будущие статьи этой серии, эта статья не предоставляет грааль, а, скорее, описывает процесс, который можно корректировать в зависимости от подхода трейдера к рынкам. Спасибо за внимание!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/11066
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования